Skip to content

Commit 89b45ed

Browse files
fix(minify): keep a constructor's this.x read a getter may answer
Reading `this` in a constructor as never throwing let an unused `this.x` drop, losing a getter's effect; terser keeps it too. Claude-Session: https://claude.ai/code/session_01ViD41i2KdwwGxrDk7yrjeD
1 parent 102fe6e commit 89b45ed

3 files changed

Lines changed: 16 additions & 47 deletions

File tree

‎lib/javascript/syntax-printer.js‎

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19778,41 +19778,6 @@ const installCorrect = (modules) => {
1977819778
}
1977919779
return false;
1978019780
};
19781-
/**
19782-
* Whether `this` where the compressor is is the object a constructor makes:
19783-
* one with no heritage, or a derived one's once `super()` has run.
19784-
* @param {Compressor} compressor the compressor
19785-
* @returns {boolean} true there
19786-
*/
19787-
const thisIsConstructed = (compressor) => {
19788-
let parent;
19789-
let viaArrow = false;
19790-
for (let i = 0; (parent = compressor.parent(i)); i++) {
19791-
if (A.isArrowNode(parent)) viaArrow = true;
19792-
if (!A.isLambdaNode(parent) || A.isArrowNode(parent)) continue;
19793-
const method = compressor.parent(i + 1);
19794-
if (!method || method.kind !== "constructor") return false;
19795-
for (let j = i + 2; (parent = compressor.parent(j)); j++) {
19796-
if (A.isClassNode(parent)) {
19797-
return (
19798-
!parent.superClass ||
19799-
(!viaArrow && superCalledBefore(compressor, i))
19800-
);
19801-
}
19802-
}
19803-
return false;
19804-
}
19805-
return false;
19806-
};
19807-
const dotThrowsOffThis = ast.dotThrows;
19808-
// A constructor's `this` is an object, never `undefined` a property read throws on.
19809-
ast.dotThrows = (node, compressor) =>
19810-
!(
19811-
A.isThisNode(node) &&
19812-
modules.improvements !== undefined &&
19813-
modules.improvements.enabled &&
19814-
thisIsConstructed(compressor)
19815-
) && dotThrowsOffThis(node, compressor);
1981619781
// terser reads `this` as pure, where a derived constructor's throws early.
1981719782
for (const wrapAnswer of [ast.wrapHasSideEffects, ast.wrapMayThrow]) {
1981819783
wrapAnswer(

‎test/helpers/oxcSmaller.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ const OXC_SMALLER_BY_REASON = [
340340
[
341341
"oxc writes conditions shorter, as oxc's `peephole/minimize_conditions.rs` tests show",
342342
[
343+
"oxc minifier: peephole/minimize_conditions.rs:1053",
343344
"oxc minifier: peephole/minimize_conditions.rs:1066",
344345
"oxc minifier: peephole/minimize_conditions.rs:1204",
345346
"oxc minifier: peephole/minimize_conditions.rs:1224",

‎test/unitCases/javascript/printer.unittest.js‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9109,17 +9109,13 @@ describe("syntax-printer", () => {
91099109
}
91109110
});
91119111

9112-
it("should write what oxc writes shorter: a catch binding, a nested `if`, a wide name, a loop update and a constructor's `this`", async () => {
9112+
it("should write what oxc writes shorter: a catch binding, a nested `if`, a wide name and a loop update", async () => {
91139113
const { minify } = await load();
91149114
for (const [input, expected] of [
91159115
["try { f(); } catch (e) { g(); }", "catch{g()}"],
91169116
["function _() { if (a) return; if (b) return c; } _();", "if(!a&&b)return c"],
91179117
['a["ÑStuffAfter"];', "a.ÑStuffAfter"],
9118-
["for (var i = 0; i < 10; i++, 0) foo(i);", "i<10;i++)"],
9119-
[
9120-
"class B extends A { constructor() { super(); if (x) this.b = 0; else this.b = 1; } }",
9121-
"super(),this.b=+!x"
9122-
]
9118+
["for (var i = 0; i < 10; i++, 0) foo(i);", "i<10;i++)"]
91239119
]) {
91249120
const { code } = await minify(input, {
91259121
compress: { passes: 2 },
@@ -9133,12 +9129,7 @@ describe("syntax-printer", () => {
91339129
["try { f(); } catch (e) { g(); }", "catch{", { ecma: 2015 }],
91349130
["try { f(); } catch (e) { var e = 1; }", "catch{", { ecma: 2020 }],
91359131
["try { f(); } catch (e) { eval('g(e)'); }", "catch{", { ecma: 2020 }],
9136-
['a["ÑStuffAfter"];', "a.Ñ", { ecma: 5 }],
9137-
[
9138-
"class B extends A { constructor() { if (x) this.b = 0; else this.b = 1; super(); } }",
9139-
"this.b=+!x",
9140-
{ ecma: 2020 }
9141-
]
9132+
['a["ÑStuffAfter"];', "a.Ñ", { ecma: 5 }]
91429133
]) {
91439134
const { code } = await minify(/** @type {string} */ (input), {
91449135
compress: { passes: 2 },
@@ -9149,6 +9140,18 @@ describe("syntax-printer", () => {
91499140
}
91509141
});
91519142

9143+
it("should keep a constructor's `this.x` read a getter may answer", async () => {
9144+
const { minify } = await load();
9145+
for (const input of [
9146+
'class A { get x() { console.log("get"); return 1; } constructor() { this.x; } } new A();',
9147+
'class A { get x() { console.log("get"); return 1; } constructor() { for (var i = 0; i < 2; i++, this.x); } } new A();',
9148+
'class B { get x() { console.log("get"); return 1; } } class A extends B { constructor() { super(); this.x; } } new A();'
9149+
]) {
9150+
const { code } = await minify(input, { compress: {}, mangle: false });
9151+
expect(runProgram(/** @type {string} */ (code))).toBe(runProgram(input));
9152+
}
9153+
});
9154+
91529155
it("should keep what `delete` and a call read through `??` once its left side is nullish", async () => {
91539156
const { minify } = await load();
91549157
for (const input of [

0 commit comments

Comments
 (0)