Title: [271120] trunk
Revision
271120
Author
[email protected]
Date
2021-01-02 11:27:42 -0800 (Sat, 02 Jan 2021)

Log Message

Improve error message for uninitialized |this| in derived constructor
https://bugs.webkit.org/show_bug.cgi?id=220221

Reviewed by Yusuke Suzuki.

JSTests:

* stress/async-arrow-functions-lexical-binding-in-class.js:
* stress/async-arrow-functions-lexical-super-binding.js:
* stress/class-derived-from-null.js:
* stress/generator-eval-this.js:
* stress/super-property-access-tdz.js:

LayoutTests/imported/w3c:

* web-platform-tests/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt:

Source/_javascript_Core:

Since class constructors perform `return this;` by default, and derived
constructors require `super()` to be called before |this| access, regular
TDZ error message is quite confusing, given the following code:

    `new (class extends Object { constructor() { } });`

Considering that currently op_check_tdz is called on thisRegister() only
in derived constructors, this patch modifies its slow path to throw a
helpful error message that covers |this| access and non-object returns.

V8 and SpiderMonkey have similar error messages, mentioning `super()`.

slow_path_throw_tdz_error is merged into slow_path_check_tdz, which is
invoked from baseline JIT, so we can reliably acquire the bytecode and
avoid code duplication.

* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/CommonSlowPaths.cpp:
(JSC::JSC_DEFINE_COMMON_SLOW_PATH):
* runtime/CommonSlowPaths.h:

LayoutTests:

* js/arrowfunction-supercall-expected.txt:
* js/arrowfunction-superproperty-expected.txt:
* js/class-syntax-extends-expected.txt:
* js/class-syntax-super-expected.txt:
* js/script-tests/arrowfunction-supercall.js:
* js/script-tests/arrowfunction-superproperty.js:
* js/script-tests/class-syntax-super.js:

Modified Paths

Diff

Modified: trunk/JSTests/ChangeLog (271119 => 271120)


--- trunk/JSTests/ChangeLog	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/JSTests/ChangeLog	2021-01-02 19:27:42 UTC (rev 271120)
@@ -1,5 +1,18 @@
 2021-01-02  Alexey Shvayka  <[email protected]>
 
+        Improve error message for uninitialized |this| in derived constructor
+        https://bugs.webkit.org/show_bug.cgi?id=220221
+
+        Reviewed by Yusuke Suzuki.
+
+        * stress/async-arrow-functions-lexical-binding-in-class.js:
+        * stress/async-arrow-functions-lexical-super-binding.js:
+        * stress/class-derived-from-null.js:
+        * stress/generator-eval-this.js:
+        * stress/super-property-access-tdz.js:
+
+2021-01-02  Alexey Shvayka  <[email protected]>
+
         Don't throw if `function.caller` is a non-strict / generator / async function
         https://bugs.webkit.org/show_bug.cgi?id=220216
 

Modified: trunk/JSTests/stress/async-arrow-functions-lexical-binding-in-class.js (271119 => 271120)


--- trunk/JSTests/stress/async-arrow-functions-lexical-binding-in-class.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/JSTests/stress/async-arrow-functions-lexical-binding-in-class.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -254,7 +254,7 @@
         // We do not care about this error
     }
     drainMicrotasks();
-    const error = asyncError.error instanceof ReferenceError && asyncError.error.toString() === 'ReferenceError: Cannot access uninitialized variable.';
+    const error = asyncError.error.toString() === `ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.`;
     if (!error) throw new Error('TDZ error is expected, but appeared:' + asyncError.error);
 }
 

Modified: trunk/JSTests/stress/async-arrow-functions-lexical-super-binding.js (271119 => 271120)


--- trunk/JSTests/stress/async-arrow-functions-lexical-super-binding.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/JSTests/stress/async-arrow-functions-lexical-super-binding.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -123,7 +123,7 @@
 shouldBe(childA4, undefined);
 shouldBe(value, 'abc');
 shouldBe(error, undefined);
-shouldBe(catchError.toString(), 'ReferenceError: Cannot access uninitialized variable.');
+shouldBe(catchError.toString(), `ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.`);
 
 catchError = undefined;
 error = undefined; 

Modified: trunk/JSTests/stress/class-derived-from-null.js (271119 => 271120)


--- trunk/JSTests/stress/class-derived-from-null.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/JSTests/stress/class-derived-from-null.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -41,7 +41,7 @@
             return this;
         }
     }
-    assertThrow(()=>(new E), 'ReferenceError: Cannot access uninitialized variable.');
+    assertThrow(()=>(new E), `ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.`);
     assert(Reflect.getPrototypeOf(E.prototype) === null);
 }
 test(test1);
@@ -116,7 +116,7 @@
     class E extends jsNull() { constructor() { let ret = this; return ret; } }
     class F extends jsNull() { constructor() { return 25; } }
     class G extends jsNull() { constructor() { super(); } }
-    assertThrow(() => Reflect.construct(E, [], D), 'ReferenceError: Cannot access uninitialized variable.');
+    assertThrow(() => Reflect.construct(E, [], D), `ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.`);
     assertThrow(() => Reflect.construct(F, [], D), 'TypeError: Cannot return a non-object type in the constructor of a derived class.');
 
     let threw = false;

Modified: trunk/JSTests/stress/generator-eval-this.js (271119 => 271120)


--- trunk/JSTests/stress/generator-eval-this.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/JSTests/stress/generator-eval-this.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -43,7 +43,7 @@
 
 shouldThrow(() => {
     new A();
-}, `ReferenceError: Cannot access uninitialized variable.`);
+}, `ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.`);
 
 class C {
     *generator()

Modified: trunk/JSTests/stress/super-property-access-tdz.js (271119 => 271120)


--- trunk/JSTests/stress/super-property-access-tdz.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/JSTests/stress/super-property-access-tdz.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -15,7 +15,7 @@
         f();
     } catch(e) {
         assert(e instanceof ReferenceError);
-        assert(e.toString() === "ReferenceError: Cannot access uninitialized variable.");
+        assert(e.toString() === `ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.`);
         threw = true;
     }
     assert(threw);

Modified: trunk/LayoutTests/ChangeLog (271119 => 271120)


--- trunk/LayoutTests/ChangeLog	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/ChangeLog	2021-01-02 19:27:42 UTC (rev 271120)
@@ -1,5 +1,20 @@
 2021-01-02  Alexey Shvayka  <[email protected]>
 
+        Improve error message for uninitialized |this| in derived constructor
+        https://bugs.webkit.org/show_bug.cgi?id=220221
+
+        Reviewed by Yusuke Suzuki.
+
+        * js/arrowfunction-supercall-expected.txt:
+        * js/arrowfunction-superproperty-expected.txt:
+        * js/class-syntax-extends-expected.txt:
+        * js/class-syntax-super-expected.txt:
+        * js/script-tests/arrowfunction-supercall.js:
+        * js/script-tests/arrowfunction-superproperty.js:
+        * js/script-tests/class-syntax-super.js:
+
+2021-01-02  Alexey Shvayka  <[email protected]>
+
         Don't throw if `function.caller` is a non-strict / generator / async function
         https://bugs.webkit.org/show_bug.cgi?id=220216
 

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (271119 => 271120)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2021-01-02 19:27:42 UTC (rev 271120)
@@ -1,3 +1,12 @@
+2021-01-02  Alexey Shvayka  <[email protected]>
+
+        Improve error message for uninitialized |this| in derived constructor
+        https://bugs.webkit.org/show_bug.cgi?id=220221
+
+        Reviewed by Yusuke Suzuki.
+
+        * web-platform-tests/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt:
+
 2020-12-30  Yusuke Suzuki  <[email protected]>
 
         [JSC] WebAssembly Table/Memory/Global should allow inheritance

Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt (271119 => 271120)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/custom-elements/parser/parser-fallsback-to-unknown-element-expected.txt	2021-01-02 19:27:42 UTC (rev 271120)
@@ -1,6 +1,6 @@
 CONSOLE MESSAGE: TypeError: The result of constructing a custom element must be a HTMLElement
 CONSOLE MESSAGE: TypeError: The result of constructing a custom element must be a HTMLElement
-CONSOLE MESSAGE: ReferenceError: Cannot access uninitialized variable.
+CONSOLE MESSAGE: ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 CONSOLE MESSAGE: Bad
 
 PASS HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns a Text node

Modified: trunk/LayoutTests/js/arrowfunction-supercall-expected.txt (271119 => 271120)


--- trunk/LayoutTests/js/arrowfunction-supercall-expected.txt	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/js/arrowfunction-supercall-expected.txt	2021-01-02 19:27:42 UTC (rev 271120)
@@ -22,8 +22,8 @@
 PASS indexOfarrowInChildConstructorInStackError > -1 && errorStack.indexOf('arrowInChildConstructor', indexOfarrowInChildConstructorInStackError + 1) === -1 is true
 PASS indexOfChildClassInStackError > -1 && errorStack.indexOf('ChildClass', indexOfChildClassInStackError + 1) === -1 is true
 PASS (new class extends A { constructor() { ((a = super())=>{})() } }).id is value
-PASS (new class extends A { constructor() { ((a = this)=>{ return a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends A { constructor() { ((a = this, b=super())=>{ return a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
+PASS (new class extends A { constructor() { ((a = this)=>{ return a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends A { constructor() { ((a = this, b=super())=>{ return a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
 PASS (new class extends A { constructor() { ((a = new.target)=>{ return a; })(); super(); } }) did not throw exception.
 PASS (new class extends A { constructor() { ((a = new.target, b=super())=>{ return a; })() } }) did not throw exception.
 PASS successfullyParsed is true

Modified: trunk/LayoutTests/js/arrowfunction-superproperty-expected.txt (271119 => 271120)


--- trunk/LayoutTests/js/arrowfunction-superproperty-expected.txt	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/js/arrowfunction-superproperty-expected.txt	2021-01-02 19:27:42 UTC (rev 271120)
@@ -5,7 +5,7 @@
 
 PASS (new B()).getValueParentFunction() is expectedValue
 PASS (new C(false)).value is expectedValue
-PASS (new C(true)) threw exception ReferenceError: Cannot access uninitialized variable..
+PASS (new C(true)) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
 PASS E.getParentStaticValue() is expectedValue
 PASS f.prop is expectedValue + "-" + expectedValue
 PASS f.prop is expectedValue + "-" + "new-value"
@@ -14,14 +14,14 @@
 PASS (new F()).genGetParentValueDeepArrow().next().value is expectedValue
 PASS (new class extends A { constructor() { ((a = super(), b = super.getValue())=>{ this.id = b; })() } }).id is expectedValue
 PASS (new class extends A { constructor() { ((a = super(), b = new.target)=>{ this.newTarget = b; })(); expectedNewTarget = new.target;} }).newTarget is expectedNewTarget
-PASS (new class extends A { constructor() { ((a = super.getValue())=>{ this.id = a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends A { constructor() { ((a = super.getValue(), b=super())=>{ this.id = a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends F { constructor() { ((a = super.prop)=>{ return a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends F { constructor() { ((a = super.prop, b=super())=>{ return a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends F { constructor() { ((a = (super.prop = "value"))=>{ this.id = a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends F { constructor() { ((a = (super.prop = "value"), b=super())=>{ this.id = a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends F { constructor() { ((a = super.genGetParentValue().next().value)=>{ this.id = a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
-PASS (new class extends F { constructor() { ((a = super.genGetParentValue().next().value, b=super())=>{ this.id = a; })() } }) threw exception ReferenceError: Cannot access uninitialized variable..
+PASS (new class extends A { constructor() { ((a = super.getValue())=>{ this.id = a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends A { constructor() { ((a = super.getValue(), b=super())=>{ this.id = a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends F { constructor() { ((a = super.prop)=>{ return a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends F { constructor() { ((a = super.prop, b=super())=>{ return a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends F { constructor() { ((a = (super.prop = "value"))=>{ this.id = a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends F { constructor() { ((a = (super.prop = "value"), b=super())=>{ this.id = a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends F { constructor() { ((a = super.genGetParentValue().next().value)=>{ this.id = a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
+PASS (new class extends F { constructor() { ((a = super.genGetParentValue().next().value, b=super())=>{ this.id = a; })() } }) threw exception ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object..
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/js/class-syntax-extends-expected.txt (271119 => 271120)


--- trunk/LayoutTests/js/class-syntax-extends-expected.txt	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/js/class-syntax-extends-expected.txt	2021-01-02 19:27:42 UTC (rev 271120)
@@ -57,7 +57,7 @@
 PASS new (class extends undefined { constructor () { this } }):::TypeError: The superclass is not a constructor.
 PASS x = undefined; new (class extends x { constructor () { super(); } }):::TypeError: The superclass is not a constructor.
 PASS class x {}; new (class extends null { constructor () { return new x; } }) instanceof x
-PASS new (class extends null { constructor () { this; } }):::ReferenceError: Cannot access uninitialized variable.
+PASS new (class extends null { constructor () { this; } }):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 PASS new (class extends null { constructor () { super(); } }):::TypeError: function is not a constructor (evaluating 'super()')
 PASS x = {}; new (class extends null { constructor () { return x } }):::x
 PASS y = 12; class C extends null { constructor () { return y; } }; new C;:::TypeError: Cannot return a non-object type in the constructor of a derived class.

Modified: trunk/LayoutTests/js/class-syntax-super-expected.txt (271119 => 271120)


--- trunk/LayoutTests/js/class-syntax-super-expected.txt	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/js/class-syntax-super-expected.txt	2021-01-02 19:27:42 UTC (rev 271120)
@@ -6,7 +6,7 @@
 PASS (new Base) instanceof Base
 PASS (new Derived) instanceof Derived
 PASS (new DerivedWithEval) instanceof DerivedWithEval
-PASS (new DerivedWithEval(true)):::ReferenceError: Cannot access uninitialized variable.
+PASS (new DerivedWithEval(true)):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 PASS (new Derived).callBaseMethod():::baseMethodValue
 PASS x = (new Derived).callBaseMethod; x():::baseMethodValue
 PASS (new Derived).callBaseMethodInGetter:::baseMethodValue
@@ -30,17 +30,17 @@
 PASS (new (class { constructor() { var arr = () => eval('super.property = "ABC"'); arr(); } })).property === "ABC"
 PASS new (class { constructor() { return undefined; } }) instanceof Object
 PASS new (class { constructor() { return 1; } }) instanceof Object
-PASS new (class extends Base { constructor() { return undefined } }):::ReferenceError: Cannot access uninitialized variable.
+PASS new (class extends Base { constructor() { return undefined } }):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 PASS new (class extends Base { constructor() { super(); return undefined } }) instanceof Object
 PASS x = { }; new (class extends Base { constructor() { return x } });:::x
 PASS x instanceof Base
-PASS new (class extends Base { constructor() { } }):::ReferenceError: Cannot access uninitialized variable.
+PASS new (class extends Base { constructor() { } }):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 PASS new (class extends Base { constructor() { return 1; } }):::TypeError: Cannot return a non-object type in the constructor of a derived class.
-PASS new (class extends null { constructor() { return undefined } }):::ReferenceError: Cannot access uninitialized variable.
+PASS new (class extends null { constructor() { return undefined } }):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 PASS new (class extends null { constructor() { super(); return undefined } }):::TypeError: function is not a constructor (evaluating 'super()')
 PASS x = { }; new (class extends null { constructor() { return x } });:::x
 PASS x instanceof Object
-PASS new (class extends null { constructor() { } }):::ReferenceError: Cannot access uninitialized variable.
+PASS new (class extends null { constructor() { } }):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 PASS new (class extends null { constructor() { return 1; } }):::TypeError: Cannot return a non-object type in the constructor of a derived class.
 PASS new (class extends null { constructor() { super() } }):::TypeError: function is not a constructor (evaluating 'super()')
 PASS new (class { constructor() { super() } }):::SyntaxError: super is not valid in this context.
@@ -56,9 +56,9 @@
 PASS new (class { constructor() { (function () { eval("super()");})(); } }):::SyntaxError: super is not valid in this context.
 PASS (new (class { method() { (function () { eval("super.method()");})(); }})).method():::SyntaxError: super is not valid in this context.
 PASS new (class extends Base { constructor() { super(); super();}}):::ReferenceError: 'super()' can't be called more than once in a constructor.
-PASS (new class D extends class { m() {}} { constructor() { eval('super["m"]()') } }):::ReferenceError: Cannot access uninitialized variable.
-PASS new class extends class { m() {}} { constructor() { super["m"](super()) } }:::ReferenceError: Cannot access uninitialized variable.
-PASS (new class D extends class { m() {}} { constructor(f) { super[f()]() } }(()=>"m")):::ReferenceError: Cannot access uninitialized variable.
+PASS (new class D extends class { m() {}} { constructor() { eval('super["m"]()') } }):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
+PASS new class extends class { m() {}} { constructor() { super["m"](super()) } }:::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
+PASS (new class D extends class { m() {}} { constructor(f) { super[f()]() } }(()=>"m")):::ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object.
 PASS (new class D extends class { m() {}} { constructor() { super(); eval('super["m"]()') } })
 PASS new class extends class { m() {}} { constructor() { super(); super["m"](super()) } }:::ReferenceError: 'super()' can't be called more than once in a constructor.
 PASS (new class D extends class { m() {}} { constructor(f) { super(); super[f()]() } }(()=>"m"))

Modified: trunk/LayoutTests/js/script-tests/arrowfunction-supercall.js (271119 => 271120)


--- trunk/LayoutTests/js/script-tests/arrowfunction-supercall.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/js/script-tests/arrowfunction-supercall.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -149,8 +149,8 @@
 shouldBeTrue("indexOfChildClassInStackError > -1 && errorStack.indexOf('ChildClass', indexOfChildClassInStackError + 1) === -1");
 
 shouldBe("(new class extends A { constructor() { ((a = super())=>{})() } }).id", "value");
-shouldThrow('(new class extends A { constructor() { ((a = this)=>{ return a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends A { constructor() { ((a = this, b=super())=>{ return a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
+shouldThrow('(new class extends A { constructor() { ((a = this)=>{ return a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends A { constructor() { ((a = this, b=super())=>{ return a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
 shouldNotThrow('(new class extends A { constructor() { ((a = new.target)=>{ return a; })(); super(); } })');
 shouldNotThrow('(new class extends A { constructor() { ((a = new.target, b=super())=>{ return a; })() } })');
 

Modified: trunk/LayoutTests/js/script-tests/arrowfunction-superproperty.js (271119 => 271120)


--- trunk/LayoutTests/js/script-tests/arrowfunction-superproperty.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/js/script-tests/arrowfunction-superproperty.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -77,7 +77,7 @@
 
 shouldBe('(new C(false)).value', 'expectedValue');
 
-shouldThrow('(new C(true))', '"ReferenceError: Cannot access uninitialized variable."');
+shouldThrow('(new C(true))', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
 
 shouldBe('E.getParentStaticValue()', 'expectedValue');
 
@@ -95,13 +95,13 @@
 shouldBe('(new class extends A { constructor() { ((a = super(), b = super.getValue())=>{ this.id = b; })() } }).id', 'expectedValue');
 var expectedNewTarget;
 shouldBe('(new class extends A { constructor() { ((a = super(), b = new.target)=>{ this.newTarget = b; })(); expectedNewTarget = new.target;} }).newTarget', 'expectedNewTarget');
-shouldThrow('(new class extends A { constructor() { ((a = super.getValue())=>{ this.id = a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends A { constructor() { ((a = super.getValue(), b=super())=>{ this.id = a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends F { constructor() { ((a = super.prop)=>{ return a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends F { constructor() { ((a = super.prop, b=super())=>{ return a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends F { constructor() { ((a = (super.prop = "value"))=>{ this.id = a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends F { constructor() { ((a = (super.prop = "value"), b=super())=>{ this.id = a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends F { constructor() { ((a = super.genGetParentValue().next().value)=>{ this.id = a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class extends F { constructor() { ((a = super.genGetParentValue().next().value, b=super())=>{ this.id = a; })() } })', '"ReferenceError: Cannot access uninitialized variable."');
+shouldThrow('(new class extends A { constructor() { ((a = super.getValue())=>{ this.id = a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends A { constructor() { ((a = super.getValue(), b=super())=>{ this.id = a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends F { constructor() { ((a = super.prop)=>{ return a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends F { constructor() { ((a = super.prop, b=super())=>{ return a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends F { constructor() { ((a = (super.prop = "value"))=>{ this.id = a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends F { constructor() { ((a = (super.prop = "value"), b=super())=>{ this.id = a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends F { constructor() { ((a = super.genGetParentValue().next().value)=>{ this.id = a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class extends F { constructor() { ((a = super.genGetParentValue().next().value, b=super())=>{ this.id = a; })() } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
 
 var successfullyParsed = true;

Modified: trunk/LayoutTests/js/script-tests/class-syntax-super.js (271119 => 271120)


--- trunk/LayoutTests/js/script-tests/class-syntax-super.js	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/LayoutTests/js/script-tests/class-syntax-super.js	2021-01-02 19:27:42 UTC (rev 271120)
@@ -88,7 +88,7 @@
 shouldBeTrue('(new Base) instanceof Base');
 shouldBeTrue('(new Derived) instanceof Derived');
 shouldBeTrue('(new DerivedWithEval) instanceof DerivedWithEval');
-shouldThrow('(new DerivedWithEval(true))', '"ReferenceError: Cannot access uninitialized variable."');
+shouldThrow('(new DerivedWithEval(true))', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
 shouldBe('(new Derived).callBaseMethod()', 'baseMethodValue');
 shouldBe('x = (new Derived).callBaseMethod; x()', 'baseMethodValue');
 shouldBe('(new Derived).callBaseMethodInGetter', 'baseMethodValue');
@@ -117,7 +117,7 @@
 shouldBeTrue('new (class extends Base { constructor() { super(); return undefined } }) instanceof Object');
 shouldBe('x = { }; new (class extends Base { constructor() { return x } });', 'x');
 shouldBeFalse('x instanceof Base');
-shouldThrow('new (class extends Base { constructor() { } })', '"ReferenceError: Cannot access uninitialized variable."');
+shouldThrow('new (class extends Base { constructor() { } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
 shouldThrow('new (class extends Base { constructor() { return 1; } })', '"TypeError: Cannot return a non-object type in the constructor of a derived class."');
 shouldThrow('new (class extends null { constructor() { return undefined } })');
 shouldThrow('new (class extends null { constructor() { super(); return undefined } })', '"TypeError: function is not a constructor (evaluating \'super()\')"');
@@ -142,9 +142,9 @@
 shouldThrow('(new (class { method() { (function () { eval("super.method()");})(); }})).method()', '"SyntaxError: super is not valid in this context."');
 
 shouldThrow('new (class extends Base { constructor() { super(); super();}})', '"ReferenceError: \'super()\' can\'t be called more than once in a constructor."');
-shouldThrow('(new class D extends class { m() {}} { constructor() { eval(\'super["m"]()\') } })', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('new class extends class { m() {}} { constructor() { super["m"](super()) } }', '"ReferenceError: Cannot access uninitialized variable."');
-shouldThrow('(new class D extends class { m() {}} { constructor(f) { super[f()]() } }(()=>"m"))', '"ReferenceError: Cannot access uninitialized variable."');
+shouldThrow('(new class D extends class { m() {}} { constructor() { eval(\'super["m"]()\') } })', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('new class extends class { m() {}} { constructor() { super["m"](super()) } }', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
+shouldThrow('(new class D extends class { m() {}} { constructor(f) { super[f()]() } }(()=>"m"))', `"ReferenceError: 'super()' must be called in derived constructor before accessing |this| or returning non-object."`);
 
 shouldNotThrow('(new class D extends class { m() {}} { constructor() { super(); eval(\'super["m"]()\') } })');
 shouldThrow('new class extends class { m() {}} { constructor() { super(); super["m"](super()) } }', '"ReferenceError: \'super()\' can\'t be called more than once in a constructor."');

Modified: trunk/Source/_javascript_Core/ChangeLog (271119 => 271120)


--- trunk/Source/_javascript_Core/ChangeLog	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/Source/_javascript_Core/ChangeLog	2021-01-02 19:27:42 UTC (rev 271120)
@@ -1,5 +1,34 @@
 2021-01-02  Alexey Shvayka  <[email protected]>
 
+        Improve error message for uninitialized |this| in derived constructor
+        https://bugs.webkit.org/show_bug.cgi?id=220221
+
+        Reviewed by Yusuke Suzuki.
+
+        Since class constructors perform `return this;` by default, and derived
+        constructors require `super()` to be called before |this| access, regular
+        TDZ error message is quite confusing, given the following code:
+
+            `new (class extends Object { constructor() { } });`
+
+        Considering that currently op_check_tdz is called on thisRegister() only
+        in derived constructors, this patch modifies its slow path to throw a
+        helpful error message that covers |this| access and non-object returns.
+
+        V8 and SpiderMonkey have similar error messages, mentioning `super()`.
+
+        slow_path_throw_tdz_error is merged into slow_path_check_tdz, which is
+        invoked from baseline JIT, so we can reliably acquire the bytecode and
+        avoid code duplication.
+
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/CommonSlowPaths.cpp:
+        (JSC::JSC_DEFINE_COMMON_SLOW_PATH):
+        * runtime/CommonSlowPaths.h:
+
+2021-01-02  Alexey Shvayka  <[email protected]>
+
         Don't throw if `function.caller` is a non-strict / generator / async function
         https://bugs.webkit.org/show_bug.cgi?id=220216
 

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (271119 => 271120)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm	2021-01-02 19:27:42 UTC (rev 271120)
@@ -838,7 +838,7 @@
     get(m_targetVirtualRegister, t0)
     loadConstantOrVariableTag(size, t0, t1)
     bineq t1, EmptyValueTag, .opNotTDZ
-    callSlowPath(_slow_path_throw_tdz_error)
+    callSlowPath(_slow_path_check_tdz)
 
 .opNotTDZ:
     dispatch()

Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (271119 => 271120)


--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm	2021-01-02 19:27:42 UTC (rev 271120)
@@ -824,7 +824,7 @@
     get(m_targetVirtualRegister, t0)
     loadConstantOrVariable(size, t0, t1)
     bqneq t1, ValueEmpty, .opNotTDZ
-    callSlowPath(_slow_path_throw_tdz_error)
+    callSlowPath(_slow_path_check_tdz)
 
 .opNotTDZ:
     dispatch()

Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp (271119 => 271120)


--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp	2021-01-02 19:27:42 UTC (rev 271120)
@@ -371,16 +371,14 @@
     RETURN_WITH_PROFILING_CUSTOM(bytecode.m_srcDst, value, PROFILE_VALUE(value));
 }
 
-JSC_DEFINE_COMMON_SLOW_PATH(slow_path_throw_tdz_error)
-{
-    BEGIN();
-    THROW(createTDZError(globalObject));
-}
-
 JSC_DEFINE_COMMON_SLOW_PATH(slow_path_check_tdz)
 {
     BEGIN();
-    THROW(createTDZError(globalObject));
+    auto bytecode = pc->as<OpCheckTdz>();
+    if (bytecode.m_targetVirtualRegister == codeBlock->thisRegister())
+        THROW(createReferenceError(globalObject, "'super()' must be called in derived constructor before accessing |this| or returning non-object."_s));
+    else
+        THROW(createTDZError(globalObject));
 }
 
 JSC_DEFINE_COMMON_SLOW_PATH(slow_path_throw_strict_mode_readonly_property_write_error)

Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h (271119 => 271120)


--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h	2021-01-02 18:41:47 UTC (rev 271119)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h	2021-01-02 19:27:42 UTC (rev 271120)
@@ -214,7 +214,6 @@
 JSC_DECLARE_COMMON_SLOW_PATH(slow_path_create_this);
 JSC_DECLARE_COMMON_SLOW_PATH(slow_path_enter);
 JSC_DECLARE_COMMON_SLOW_PATH(slow_path_to_this);
-JSC_DECLARE_COMMON_SLOW_PATH(slow_path_throw_tdz_error);
 JSC_DECLARE_COMMON_SLOW_PATH(slow_path_check_tdz);
 JSC_DECLARE_COMMON_SLOW_PATH(slow_path_throw_strict_mode_readonly_property_write_error);
 JSC_DECLARE_COMMON_SLOW_PATH(slow_path_not);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to