diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit-test/tests/class | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/tests/class')
53 files changed, 1672 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/class/bug1169746.js b/js/src/jit-test/tests/class/bug1169746.js new file mode 100644 index 0000000000..1e63ca48f9 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1169746.js @@ -0,0 +1,10 @@ +class C { } +class D extends C { } + +function f() +{ + for (var i = 0; i < 2000; ++i) + new D(); +} + +f(); diff --git a/js/src/jit-test/tests/class/bug1357506.js b/js/src/jit-test/tests/class/bug1357506.js new file mode 100644 index 0000000000..2e6f54ef38 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1357506.js @@ -0,0 +1,16 @@ +// Test that constructors that abort due to asm.js do not assert due to the +// parser keeping track of the FunctionBox corresponding to the constructor. + +class a { + constructor() { + "use asm"; + } +} + +function f() { + class a { + constructor() { + "use asm"; + } + } +} diff --git a/js/src/jit-test/tests/class/bug1359622.js b/js/src/jit-test/tests/class/bug1359622.js new file mode 100644 index 0000000000..cbea4a4f7c --- /dev/null +++ b/js/src/jit-test/tests/class/bug1359622.js @@ -0,0 +1,4 @@ +var g = newGlobal({ discardSource: true }); +g.evaluate(` + unescape(class get { static staticMethod() {} }); +`); diff --git a/js/src/jit-test/tests/class/bug1473272-default-constructors.js b/js/src/jit-test/tests/class/bug1473272-default-constructors.js new file mode 100644 index 0000000000..e9bd5c1889 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1473272-default-constructors.js @@ -0,0 +1,24 @@ +// Test the source location info in a derived-class default constructor. + +function W() { test(); } +class Z extends W {} // line 4 +class Y extends Z {} // line 5 + +class X extends Y {} // line 7 + +function test() { + for (let frame of new Error().stack.split('\n')) { + function lineNumber(frame) { + return +frame.match(/(\d+):\d+$/)[1]; + } + + if (frame.startsWith("Z@")) + assertEq(lineNumber(frame), 4); + if (frame.startsWith("Y@")) + assertEq(lineNumber(frame), 5); + if (frame.startsWith("X@")) + assertEq(lineNumber(frame), 7); + } +} + +new X; diff --git a/js/src/jit-test/tests/class/bug1488385.js b/js/src/jit-test/tests/class/bug1488385.js new file mode 100644 index 0000000000..46a15c0611 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1488385.js @@ -0,0 +1,13 @@ +// Default class constructors should no longer be marked as self-hosted +// functions. They should be munged to appear in every respect as if they +// originated with the class definition. + +load(libdir + 'asserts.js'); + +function f() { + return f.caller.p; +} + +// Since default constructors are strict mode code, this should get: +// TypeError: access to strict mode caller function is censored +assertThrowsInstanceOf(() => new class extends f {}, TypeError); diff --git a/js/src/jit-test/tests/class/bug1567579.js b/js/src/jit-test/tests/class/bug1567579.js new file mode 100644 index 0000000000..240279ceda --- /dev/null +++ b/js/src/jit-test/tests/class/bug1567579.js @@ -0,0 +1,4 @@ +var res = "class { constructor() {} }"; +var test = eval("(" + res + ").toString()"); + +assertEq(test, res);
\ No newline at end of file diff --git a/js/src/jit-test/tests/class/bug1616535.js b/js/src/jit-test/tests/class/bug1616535.js new file mode 100644 index 0000000000..feeec19e65 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1616535.js @@ -0,0 +1,3 @@ +// |jit-test| error:TypeError + +class C extends (/x/) {} diff --git a/js/src/jit-test/tests/class/bug1628719.js b/js/src/jit-test/tests/class/bug1628719.js new file mode 100644 index 0000000000..76d3b97664 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1628719.js @@ -0,0 +1,28 @@ +class BaseOne { + static build() { return 'BaseOne'; } +} + +class BaseTwo { + static build() { return 'BaseTwo'; } +} + +class ClassOne extends BaseOne { + constructor() { super(); } +} + +class ClassTwo extends BaseTwo { + constructor() { super(); } +} + +const ClassMap = { 1: ClassOne, 2: ClassTwo }; +const TimeLine = [2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2]; + +function run() { + for (var i = 0; i < TimeLine.length; ++i) { + var j = TimeLine[i]; + var expected = j === 1 ? 'BaseOne' : 'BaseTwo'; + var actual = ClassMap[j].build(); + assertEq(actual, expected); + } +} +run(); diff --git a/js/src/jit-test/tests/class/bug1645835.js b/js/src/jit-test/tests/class/bug1645835.js new file mode 100644 index 0000000000..3a66d94eec --- /dev/null +++ b/js/src/jit-test/tests/class/bug1645835.js @@ -0,0 +1,18 @@ +function Base() { + // Creates MNewObject, which is recoverable. An instruction which has the + // |RecoveredOnBailout| flag set mustn't have any live uses. + return {}; +} + +class C extends Base { + constructor() { + // |super()| assigns to |this|. The |this| slot mustn't be optimised away + // in case the debugger tries to read that slot. + super(); + } +} + +for (var i = 0; i < 100; i++) { + // The returned object is not used, so it can be optimised away. + new C(); +} diff --git a/js/src/jit-test/tests/class/bug1709328.js b/js/src/jit-test/tests/class/bug1709328.js new file mode 100644 index 0000000000..9ad856a5e4 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1709328.js @@ -0,0 +1,18 @@ +class A0 { constructor() { this.dummy = true; } } +class A1 { constructor() { this.dummy = true; } } +class A2 { constructor() { this.dummy = true; } } +class A3 { constructor() { this.dummy = true; } } +class A4 { constructor() { this.dummy = true; } } +class A5 { constructor() { this.dummy = true; } } +class A6 { constructor() { this.dummy = true; } } +class A7 { constructor() { this.dummy = true; } } +class A8 { constructor() { this.dummy = true; } } +class A9 { constructor() { this.dummy = true; } } + +var constructors = [A1, A2, A3, A4, A5, A6, A7, A8, A9]; +for (var i = 0; i < 1000; i++) { + for (var construct of constructors) { + var h = new construct(); + assertEq(Reflect.get(h, "nonexistent", "dummy"), undefined); + } +} diff --git a/js/src/jit-test/tests/class/bug1715318.js b/js/src/jit-test/tests/class/bug1715318.js new file mode 100644 index 0000000000..6513cfeb8a --- /dev/null +++ b/js/src/jit-test/tests/class/bug1715318.js @@ -0,0 +1,18 @@ +// |jit-test| --no-threads; --fast-warmup +function f() { + // A non-constructor with a null .prototype. + let arrow = _ => null; + arrow.prototype = null; + + // Warm up. + for (var i = 0; i < 100; i++) {} + + try { + class C1 extends arrow {} + throw "Fail"; + } catch (e) { + assertEq(e instanceof TypeError, true); + } +} +f(); +f(); diff --git a/js/src/jit-test/tests/class/bug1720032-1.js b/js/src/jit-test/tests/class/bug1720032-1.js new file mode 100644 index 0000000000..c2ab722638 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1720032-1.js @@ -0,0 +1,29 @@ +// |jit-test| skip-if: getBuildConfiguration()['osx'] && getBuildConfiguration()['arm64'] +load(libdir + "asserts.js"); +function main() { + class Base {} + + class Derived extends Base { + constructor() { + super(); + + let v = 0xffff; + + try { + // Ensure this statement doesn't get DCE'ed. + v &= 0xff; + + // Returning a primitive value throws. + return 0; + } catch {} + + assertEq(v, 255); + } + } + + for (let i = 0; i < 15; i++) { + assertThrowsInstanceOf(() => new Derived(), TypeError); + } +} +main(); +main(); diff --git a/js/src/jit-test/tests/class/bug1720032-2.js b/js/src/jit-test/tests/class/bug1720032-2.js new file mode 100644 index 0000000000..7ca7a8df06 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1720032-2.js @@ -0,0 +1,27 @@ +function main() { + class Base {} + + class Derived extends Base { + constructor() { + let v = 0xffff; + + try { + // Ensure this statement doesn't get DCE'ed. + v &= 0xff; + + // Accessing |this| throws when |super()| wasn't yet called. + this; + } catch {} + + assertEq(v, 255); + + super(); + } + } + + for (let i = 0; i < 15; i++) { + new Derived(); + } +} +main(); +main(); diff --git a/js/src/jit-test/tests/class/bug1720032-3.js b/js/src/jit-test/tests/class/bug1720032-3.js new file mode 100644 index 0000000000..e087e8e0f8 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1720032-3.js @@ -0,0 +1,27 @@ +function main() { + class Base {} + + class Derived extends Base { + constructor() { + super(); + + let v = 0xffff; + + try { + // Ensure this statement doesn't get DCE'ed. + v &= 0xff; + + // Calling |super()| twice throws an error. + super(); + } catch {} + + assertEq(v, 255); + } + } + + for (let i = 0; i < 15; i++) { + new Derived(); + } +} +main(); +main(); diff --git a/js/src/jit-test/tests/class/bug1727281.js b/js/src/jit-test/tests/class/bug1727281.js new file mode 100644 index 0000000000..e17bec8612 --- /dev/null +++ b/js/src/jit-test/tests/class/bug1727281.js @@ -0,0 +1,4 @@ +// No assertion. +var a = { + 0: class { #$() {} } +}; diff --git a/js/src/jit-test/tests/class/checkreturn-catch-return-finally-super-arrow.js b/js/src/jit-test/tests/class/checkreturn-catch-return-finally-super-arrow.js new file mode 100644 index 0000000000..4a9d22955a --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-catch-return-finally-super-arrow.js @@ -0,0 +1,22 @@ +class C extends class {} { + constructor() { + var f = () => super(); + + try { + throw null; + } catch { + return; + } finally { + f(); + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + // No error. + new C(); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-catch-return-finally-super.js b/js/src/jit-test/tests/class/checkreturn-catch-return-finally-super.js new file mode 100644 index 0000000000..534d8e3046 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-catch-return-finally-super.js @@ -0,0 +1,20 @@ +class C extends class {} { + constructor() { + try { + throw null; + } catch { + return; + } finally { + super(); + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + // No error. + new C(); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-catch-return.js b/js/src/jit-test/tests/class/checkreturn-catch-return.js new file mode 100644 index 0000000000..9794dc23b1 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-catch-return.js @@ -0,0 +1,21 @@ +load(libdir + "asserts.js"); + +class C extends class {} { + constructor() { + super(); + + try { + return 0; + } catch { + return; + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), TypeError); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-catch-super-arrow.js b/js/src/jit-test/tests/class/checkreturn-catch-super-arrow.js new file mode 100644 index 0000000000..0645c51e23 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-catch-super-arrow.js @@ -0,0 +1,21 @@ +load(libdir + "asserts.js"); + +class C extends class {} { + constructor() { + var f = () => super(); + + try { + return 0; + } catch { + f(); + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), TypeError); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-catch-super.js b/js/src/jit-test/tests/class/checkreturn-catch-super.js new file mode 100644 index 0000000000..e06270a527 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-catch-super.js @@ -0,0 +1,19 @@ +load(libdir + "asserts.js"); + +class C extends class {} { + constructor() { + try { + return 0; + } catch { + super(); + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), TypeError); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-finally-super-arrow.js b/js/src/jit-test/tests/class/checkreturn-finally-super-arrow.js new file mode 100644 index 0000000000..91d43a3507 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-finally-super-arrow.js @@ -0,0 +1,20 @@ +class C extends class {} { + constructor() { + var f = () => super(); + + try { + return; + } finally { + f(); + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + // No error. + new C(); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-finally-super.js b/js/src/jit-test/tests/class/checkreturn-finally-super.js new file mode 100644 index 0000000000..805b9af50f --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-finally-super.js @@ -0,0 +1,18 @@ +class C extends class {} { + constructor() { + try { + return; + } finally { + super(); + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + // No error. + new C(); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-for-condition.js b/js/src/jit-test/tests/class/checkreturn-for-condition.js new file mode 100644 index 0000000000..e654056a15 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-for-condition.js @@ -0,0 +1,68 @@ +load(libdir + "asserts.js"); + +function testReturn() { + class C extends class {} { + constructor(n) { + for (let i = 0; i < n; ++i) { + return; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(1), ReferenceError); + } +} +testReturn(); + +function testReturnSuper() { + class C extends class {} { + constructor(n) { + super(); + for (let i = 0; i < n; ++i) { + return; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + // No error. + new C(1); + } +} +testReturnSuper(); + +function testReturnPrimitive() { + class C extends class {} { + constructor(n) { + for (let i = 0; i < n; ++i) { + return 0; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(1), TypeError); + } +} +testReturnPrimitive(); + +function testReturnPrimitiveSuper() { + class C extends class {} { + constructor(n) { + super(); + for (let i = 0; i < n; ++i) { + return 0; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(1), TypeError); + } +} +testReturnPrimitiveSuper(); diff --git a/js/src/jit-test/tests/class/checkreturn-for-of-arrow.js b/js/src/jit-test/tests/class/checkreturn-for-of-arrow.js new file mode 100644 index 0000000000..4358bb1778 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-for-of-arrow.js @@ -0,0 +1,29 @@ +var iter = { + [Symbol.iterator]() { return this; }, + next() { return {done: false}; }, + return() { + // Calls |super()|. + this.f(); + + return {done: true}; + }, +}; + +class C extends class {} { + constructor() { + iter.f = () => super(); + + for (var k of iter) { + return; + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + // No error. + new C(); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-for-of.js b/js/src/jit-test/tests/class/checkreturn-for-of.js new file mode 100644 index 0000000000..2c06fd4ea0 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-for-of.js @@ -0,0 +1,27 @@ +load(libdir + "asserts.js"); + +var error = {}; + +var iter = { + [Symbol.iterator]() { return this; }, + next() { return {done: false}; }, + return() { throw error; }, +}; + +class C extends class {} { + constructor() { + super(); + + for (var k of iter) { + return 0; + } + } +} + +function test() { + for (var i = 0; i < 100; ++i) { + assertThrowsValue(() => new C(), error); + } +} + +test(); diff --git a/js/src/jit-test/tests/class/checkreturn-for.js b/js/src/jit-test/tests/class/checkreturn-for.js new file mode 100644 index 0000000000..c1efd30540 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-for.js @@ -0,0 +1,68 @@ +load(libdir + "asserts.js"); + +function testReturn() { + class C extends class {} { + constructor() { + for (;;) { + return; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), ReferenceError); + } +} +testReturn(); + +function testReturnSuper() { + class C extends class {} { + constructor() { + super(); + for (;;) { + return; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + // No error. + new C(); + } +} +testReturnSuper(); + +function testReturnPrimitive() { + class C extends class {} { + constructor() { + for (;;) { + return 0; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), TypeError); + } +} +testReturnPrimitive(); + +function testReturnPrimitiveSuper() { + class C extends class {} { + constructor() { + super(); + for (;;) { + return 0; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), TypeError); + } +} +testReturnPrimitiveSuper(); diff --git a/js/src/jit-test/tests/class/checkreturn-optimized-out.js b/js/src/jit-test/tests/class/checkreturn-optimized-out.js new file mode 100644 index 0000000000..10025c9c27 --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-optimized-out.js @@ -0,0 +1,23 @@ +function h() {} + +class Base {} + +class C extends Base { + constructor(cond) { + if (cond) { + // Never entered. + for (var i = 0; i < args.length; ++i) h(); + } + super(); + } +} + +function f() { + for (var i = 0; i < 10; i++) { + var x = new C(false); + } +} + +for (var i = 0; i < 3; i++) { + f(); +} diff --git a/js/src/jit-test/tests/class/checkreturn-source-location.js b/js/src/jit-test/tests/class/checkreturn-source-location.js new file mode 100644 index 0000000000..3bdc83c36d --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-source-location.js @@ -0,0 +1,19 @@ +// Test source location for missing-super-call check at the end of a derived class constructor. +class A {}; +class B extends A { + constructor(x) { + if (x === null) { + throw "fail"; + } + } +}; +let ex; +try { + new B(); +} catch (e) { + ex = e; +} +assertEq(ex instanceof ReferenceError, true); +// The closing '}' of B's constructor. +assertEq(ex.lineNumber, 8); +assertEq(ex.columnNumber, 5); diff --git a/js/src/jit-test/tests/class/checkreturn-while.js b/js/src/jit-test/tests/class/checkreturn-while.js new file mode 100644 index 0000000000..426e7effdc --- /dev/null +++ b/js/src/jit-test/tests/class/checkreturn-while.js @@ -0,0 +1,68 @@ +load(libdir + "asserts.js"); + +function testReturn() { + class C extends class {} { + constructor() { + while (true) { + return; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), ReferenceError); + } +} +testReturn(); + +function testReturnSuper() { + class C extends class {} { + constructor() { + super(); + while (true) { + return; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + // No error. + new C(); + } +} +testReturnSuper(); + +function testReturnPrimitive() { + class C extends class {} { + constructor() { + while (true) { + return 0; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), TypeError); + } +} +testReturnPrimitive(); + +function testReturnPrimitiveSuper() { + class C extends class {} { + constructor() { + super(); + while (true) { + return 0; + } + assertEq(true, false, "unreachable"); + } + } + + for (var i = 0; i < 100; ++i) { + assertThrowsInstanceOf(() => new C(), TypeError); + } +} +testReturnPrimitiveSuper(); diff --git a/js/src/jit-test/tests/class/class-static-01.js b/js/src/jit-test/tests/class/class-static-01.js new file mode 100644 index 0000000000..ee41def1fb --- /dev/null +++ b/js/src/jit-test/tests/class/class-static-01.js @@ -0,0 +1,172 @@ +// |jit-test| +load(libdir + "asserts.js"); + +// Parse +class A { + static { } +}; + +// Run +var ran = false; +class B { + static { + ran = true; + } +}; + +assertEq(ran, true); + +// Can access static assigned before. +var res; +class C { + static x = 10; + static { + res = this.x; + } +}; + +assertEq(res, 10); + +// Multiple static blocks +class D { + static { } + static { } +}; + +class D1 { + static { } static { } +}; + +// Blocks run in order. +class E { + static { + res = 10; + } + static { + assertEq(res, 10); + res = 14; + } +} + +assertEq(res, 14); + + +// Can use static to access private state. +let accessor; +class F { + #x = 10 + static { + accessor = (o) => o.#x; + } +} + +let f = new F; +assertEq(accessor(f), 10); + +class G { + static { + this.a = 10; + } +} +assertEq(G.a, 10); + +// Can use the super-keyword to access a static method in super class. +class H { + static a() { return 101; } +} + +class I extends H { + static { + res = super.a(); + } +} + +assertEq(res, 101); + +// Can add a property to a class from within a static +class J { + static { + this.x = 12; + } +} + +assertEq(J.x, 12); + + +function assertThrowsSyntaxError(str) { + assertThrowsInstanceOf(() => eval(str), SyntaxError); // Direct Eval + assertThrowsInstanceOf(() => (1, eval)(str), SyntaxError); // Indirect Eval + assertThrowsInstanceOf(() => Function(str), SyntaxError); // Function +} + +assertThrowsSyntaxError(` +class X { + static { + arguments; + } +} +`) + + +assertThrowsSyntaxError(` +class X extends class {} { + static { + super(); // can't call super in a static block + } +} +`) + +assertThrowsSyntaxError(` +class X { + static { + return 10; // Can't return in a static block + } +} +`) + +assertThrowsSyntaxError(` +class X { + static { + await 10; // Can't await in a static block + } +} +`) + +// Can't await inside a static block, even if we're inside an async function. +// +// The error message here is not good `SyntaxError: await is a reserved identifier`, +// but can be fixed later. s +assertThrowsSyntaxError(` +async function f() { + class X { + static { + await 10; // Can't await in a static block + } + } +} +`) + + +assertThrowsSyntaxError(` +class X { + static { + yield 10; // Can't yield in a static block + } +} +`) + + +assertThrowsSyntaxError(` +function* gen() { + class X { + static { + yield 10; // Can't yield in a static block, even inside a generator + } + } +} +`) + +// Incomplete should throw a sensible error. +assertThrowsSyntaxError(` +class A { static { this.x +`)
\ No newline at end of file diff --git a/js/src/jit-test/tests/class/class-static-02.js b/js/src/jit-test/tests/class/class-static-02.js new file mode 100644 index 0000000000..e096ec86d1 --- /dev/null +++ b/js/src/jit-test/tests/class/class-static-02.js @@ -0,0 +1,10 @@ +// |jit-test| + +Reflect.parse(`class A { + static { print('hi'); } +}`) + +Reflect.parse(`class A { + static x = 10; + static { this.x++ } +}`);
\ No newline at end of file diff --git a/js/src/jit-test/tests/class/class-static-03.js b/js/src/jit-test/tests/class/class-static-03.js new file mode 100644 index 0000000000..816e63d923 --- /dev/null +++ b/js/src/jit-test/tests/class/class-static-03.js @@ -0,0 +1,15 @@ +// |jit-test| + +var g = newGlobal({ newCompartment: true }); +var dbg = new Debugger; +var gw = dbg.addDebuggee(g); + +dbg.onDebuggerStatement = function (frame) { + var e = frame.eval("this.y = 13"); + return undefined; +}; + +g.eval("class A { static x = 10; static { debugger; } }; a = A;"); +assertEq(g.a.x, 10); +assertEq(g.a.y, 13); + diff --git a/js/src/jit-test/tests/class/classconstructor.js b/js/src/jit-test/tests/class/classconstructor.js new file mode 100644 index 0000000000..2d926b48ca --- /dev/null +++ b/js/src/jit-test/tests/class/classconstructor.js @@ -0,0 +1,27 @@ +function customconstructor() { + class X { + constructor() {} + a() {} + }; + + assertEq(Object.getOwnPropertyDescriptor(X, "prototype").configurable, false); + assertEq(Object.getOwnPropertyDescriptor(X.prototype, "constructor").enumerable, false); +} + +function defaultconstructor() { + class X { + a() {} + }; + + assertEq(Object.getOwnPropertyDescriptor(X, "prototype").configurable, false); + assertEq(Object.getOwnPropertyDescriptor(X.prototype, "constructor").enumerable, false); +} + +function run() { + for (var i = 0; i < 100; i++) { + customconstructor(); + defaultconstructor(); + } +} + +run(); diff --git a/js/src/jit-test/tests/class/compProp.js b/js/src/jit-test/tests/class/compProp.js new file mode 100644 index 0000000000..f98727c718 --- /dev/null +++ b/js/src/jit-test/tests/class/compProp.js @@ -0,0 +1,15 @@ +load(libdir + "asserts.js"); + +function f(tag) { return {[tag]: 1}; } +a = []; +for (var i = 0; i < 2000; i++) + a[i] = f("first"); + +for (var i = 0; i < 2000; i++) + assertEq(a[i].first, 1); + +for (var i = 0; i < 2000; i++) + a[i] = f("second"); + +for (var i = 0; i < 2000; i++) + assertEq(a[i].second, 1); diff --git a/js/src/jit-test/tests/class/default-constructor-position.js b/js/src/jit-test/tests/class/default-constructor-position.js new file mode 100644 index 0000000000..abe2eeb7ab --- /dev/null +++ b/js/src/jit-test/tests/class/default-constructor-position.js @@ -0,0 +1,68 @@ +// Test default class constructors have reasonable lineno/column values + +const source = ` + /* GeneralParser::synthesizeConstructor */ class A { + } + + /* GeneralParser::synthesizeConstructor (derived) */ class B extends A { + } + + /* GeneralParser::synthesizeConstructor */ class C { + field = "default value"; + } + + /* GeneralParser::synthesizeConstructor (derived) */ class D extends A { + field = "default value"; + } +`; + +// Use the Debugger API to introspect the line / column. +let d = new Debugger(); +let g = newGlobal({newCompartment: true}) +let gw = d.addDebuggee(g); + +g.evaluate(source); + +function getStartLine(name) { + return gw.makeDebuggeeValue(g.eval(name)).script.startLine; +} + +function getStartColumn(name) { + return gw.makeDebuggeeValue(g.eval(name)).script.startColumn; +} + +function getSourceStart(name) { + return gw.makeDebuggeeValue(g.eval(name)).script.sourceStart; +} + +function getSourceLength(name) { + return gw.makeDebuggeeValue(g.eval(name)).script.sourceLength; +} + +// Compute the expected line/column from source. +matches = ""; +lineno = 0; +for (text of source.split("\n")) { + lineno++; + + column = text.indexOf("class"); + if (column < 0) { + continue; + } + + className = text[column + 6]; + matches += className; + + // Check lineno/column. + assertEq(getStartLine(className), lineno); + assertEq(getStartColumn(className), column); + + // Check sourceStart/sourceEnd. + offset = source.indexOf("class " + className) + length = source.substring(offset).indexOf("}") + 1 + assertEq(getSourceStart(className), offset) + assertEq(getSourceLength(className), length) +} + +// Sanity check to did actual matches +assertEq(matches, "ABCD"); diff --git a/js/src/jit-test/tests/class/methDefn.js b/js/src/jit-test/tests/class/methDefn.js new file mode 100644 index 0000000000..f8a6d883a4 --- /dev/null +++ b/js/src/jit-test/tests/class/methDefn.js @@ -0,0 +1,15 @@ +load(libdir + "asserts.js"); + +function f(tag) { return {[tag](){return 1;}}; } +a = []; +for (var i = 0; i < 2000; i++) + a[i] = f("first"); + +for (var i = 0; i < 2000; i++) + assertEq(a[i].first(), 1); + +for (var i = 0; i < 2000; i++) + a[i] = f("second"); + +for (var i = 0; i < 2000; i++) + assertEq(a[i].second(), 1); diff --git a/js/src/jit-test/tests/class/regress-merge-descriptors-simple.js b/js/src/jit-test/tests/class/regress-merge-descriptors-simple.js new file mode 100644 index 0000000000..57ea9a9f3a --- /dev/null +++ b/js/src/jit-test/tests/class/regress-merge-descriptors-simple.js @@ -0,0 +1,37 @@ +// Copyright 2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var extend = function (d, b) { + function foo() { this.constructor = d; } + foo.prototype = b.prototype; + d.prototype = new foo(); +}; + +var Car = (function (Super) { + var Car = function () {} + extend(Car, Super); +}(Object)); diff --git a/js/src/jit-test/tests/class/regress-merge-descriptors.js b/js/src/jit-test/tests/class/regress-merge-descriptors.js new file mode 100644 index 0000000000..f56adcabd2 --- /dev/null +++ b/js/src/jit-test/tests/class/regress-merge-descriptors.js @@ -0,0 +1,92 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +var extend = function (d, b) { + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; + +var Car = (function (Super) { + var Car = function () { + var self = this; + + Super.call(self); + + Object.defineProperties(self, { + "make": { + enumerable: true, + configurable: true, + get: function () { + return "Ford"; + } + } + }); + + self.copy = function () { + throw new Error("Meant to be overriden"); + }; + + return self; + }; + + extend(Car, Super); + + return Car; +}(Object)); + + +var SuperCar = ((function (Super) { + var SuperCar = function (make) { + var self = this; + + Super.call(self); + + + Object.defineProperties(self, { + "make": { + enumerable: true, + configurable: true, + get: function () { + return make; + } + } + }); + + // Convert self.copy from DATA_CONSTANT to DATA. + self.copy = function () { }; + + return self; + }; + extend(SuperCar, Super); + return SuperCar; +})(Car)); + +assertEq("Ford", new Car().make); +assertEq("Bugatti", new SuperCar("Bugatti").make); +assertEq("Lambo", new SuperCar("Lambo").make); +assertEq("Shelby", new SuperCar("Shelby").make); diff --git a/js/src/jit-test/tests/class/relazify-constructor.js b/js/src/jit-test/tests/class/relazify-constructor.js new file mode 100644 index 0000000000..2066898886 --- /dev/null +++ b/js/src/jit-test/tests/class/relazify-constructor.js @@ -0,0 +1,22 @@ +class WithConstructor { + constructor() {} +} + +class DefaultConstructor { +} + +class WithConstructorDerived extends DefaultConstructor { + constructor() { + super() + } +} + +class DefaultConstructorDerived extends DefaultConstructor { +} + +relazifyFunctions(); + +let a = new WithConstructor; +let b = new DefaultConstructor; +let c = new WithConstructorDerived; +let d = new DefaultConstructorDerived; diff --git a/js/src/jit-test/tests/class/super-base-is-null-get-elem.js b/js/src/jit-test/tests/class/super-base-is-null-get-elem.js new file mode 100644 index 0000000000..3bb052af54 --- /dev/null +++ b/js/src/jit-test/tests/class/super-base-is-null-get-elem.js @@ -0,0 +1,12 @@ +class NullProto { + static m(key) { + try { + return super[key]; + } catch {} + } +} +Object.setPrototypeOf(NullProto, null); + +for (var i = 0; i < 100; ++i) { + NullProto.m(i); +} diff --git a/js/src/jit-test/tests/class/super-base-is-null-get-prop.js b/js/src/jit-test/tests/class/super-base-is-null-get-prop.js new file mode 100644 index 0000000000..eff5a290ee --- /dev/null +++ b/js/src/jit-test/tests/class/super-base-is-null-get-prop.js @@ -0,0 +1,12 @@ +class NullProto { + static m() { + try { + return super.p; + } catch {} + } +} +Object.setPrototypeOf(NullProto, null); + +for (var i = 0; i < 100; ++i) { + NullProto.m(); +} diff --git a/js/src/jit-test/tests/class/super-base-is-null-set-elem.js b/js/src/jit-test/tests/class/super-base-is-null-set-elem.js new file mode 100644 index 0000000000..226760f9db --- /dev/null +++ b/js/src/jit-test/tests/class/super-base-is-null-set-elem.js @@ -0,0 +1,12 @@ +class NullProto { + static m(key) { + try { + super[key] = 0; + } catch {} + } +} +Object.setPrototypeOf(NullProto, null); + +for (var i = 0; i < 100; ++i) { + NullProto.m(i); +} diff --git a/js/src/jit-test/tests/class/super-base-is-null-set-prop.js b/js/src/jit-test/tests/class/super-base-is-null-set-prop.js new file mode 100644 index 0000000000..a3b734eef2 --- /dev/null +++ b/js/src/jit-test/tests/class/super-base-is-null-set-prop.js @@ -0,0 +1,12 @@ +class NullProto { + static m() { + try { + super.p = 0; + } catch {} + } +} +Object.setPrototypeOf(NullProto, null); + +for (var i = 0; i < 100; ++i) { + NullProto.m(); +} diff --git a/js/src/jit-test/tests/class/super-in-nested-eval.js b/js/src/jit-test/tests/class/super-in-nested-eval.js new file mode 100644 index 0000000000..8786de4ba7 --- /dev/null +++ b/js/src/jit-test/tests/class/super-in-nested-eval.js @@ -0,0 +1,34 @@ +// |jit-test| allow-overrecursed + +// Test super() and super.foo() calls in deeply nested eval. + +class C { + constructor(x) { this.x = x; } + foo() { this.x++; } +} +class D extends C { + constructor(depth) { + var d = depth; + var callsuper = 'super(depth); super.foo();'; + var s = "var q; if (d-- > 0) { eval(s) } else { eval(callsuper); }"; + eval(s); + } +} + +const MAX_DEPTH = 250; + +// These values should work. +var depths = [0, 1, 10, 200, MAX_DEPTH]; +for (var d of depths) { + var o = new D(d); + assertEq(o.x, d + 1); +} + +// This should fail. +var ex; +try { + new D(MAX_DEPTH + 1); +} catch(e) { + ex = e; +} +assertEq(ex instanceof InternalError, true); diff --git a/js/src/jit-test/tests/class/super-this-env.js b/js/src/jit-test/tests/class/super-this-env.js new file mode 100644 index 0000000000..9faca9b745 --- /dev/null +++ b/js/src/jit-test/tests/class/super-this-env.js @@ -0,0 +1,34 @@ +for (let forceFullParse of [true, false]) { + assertEq(Object.prototype.toString, evaluate(`{ + class C extends Object { + f() { + let x = "toString"; + return () => super[x]; + } + } + + (new C).f()() + }`, { forceFullParse })); + + assertEq(Object.prototype.toString, evaluate(`{ + class C extends Object { + f() { + let x = "toString"; + return () => eval("super[x]"); + } + } + + (new C).f()() + }`, { forceFullParse })); + + assertEq(Object.prototype.toString, evaluate(`{ + class C extends Object { + f() { + let x = "toString"; + return () => eval("() => super[x]"); + } + } + + (new C).f()()() + }`, { forceFullParse })); +} diff --git a/js/src/jit-test/tests/class/superElemMegamorphic.js b/js/src/jit-test/tests/class/superElemMegamorphic.js new file mode 100644 index 0000000000..8601cc472a --- /dev/null +++ b/js/src/jit-test/tests/class/superElemMegamorphic.js @@ -0,0 +1,33 @@ +class C { }; +C.prototype.a = "a"; +C.prototype.b = "b"; +C.prototype.c = "c"; +C.prototype.d = "d"; +C.prototype.e = "e"; +C.prototype.f = "f"; +C.prototype.g = "g"; +C.prototype.h = "h"; +C.prototype.i = "i"; +C.prototype.j = "j"; +C.prototype.k = "k"; +C.prototype.l = "l"; +C.prototype.m = "m"; +C.prototype.n = "n"; +C.prototype.o = "o"; +C.prototype.p = "p"; +C.prototype.q = "q"; +C.prototype.r = "r"; + +class D extends C { + foo(p) { + return super[p]; + } +} + +var d = new D(); + +for (let i = 0; i < 20; ++i) { + for (let p in C.prototype) { + assertEq(p, d.foo(p)); + } +} diff --git a/js/src/jit-test/tests/class/superProp.js b/js/src/jit-test/tests/class/superProp.js new file mode 100644 index 0000000000..37b85ab652 --- /dev/null +++ b/js/src/jit-test/tests/class/superProp.js @@ -0,0 +1,62 @@ +var g_get_this = "get_this"; +var g_prop_this = "prop_this"; + +class Base +{ + get get_prop() { return 7; } + get get_this() { return this; } + prop_call() { return 11; } + prop_this() { return this.x; } +} +Base.prototype.prop_proto = 5; +Base.prototype.x = (-1); +Base.prototype[0] = 100; +Base.prototype[1] = 101; +Base.prototype[2] = 102; + +class Derived extends Base +{ + get get_prop() { throw "Bad"; } + get get_this() { throw "Bad"; } + prop_call() { throw "Bad"; } + prop_this() { throw "Bad"; } + + do_test_getprop() + { + this.x = 13; + + assertEq(super.prop_proto, 5); + + assertEq(super.get_prop, 7); + assertEq(super.get_this, this); + + assertEq(super.prop_call(), 11); + assertEq(super.prop_this(), 13); + } + + do_test_getelem() + { + this.x = 13; + + assertEq(super[g_get_this], this); + + assertEq(super[g_prop_this](), 13); + assertEq(super[0], 100); + assertEq(super[1], 101); + assertEq(super[2], 102); + } +} +Derived.prototype.prop_proto = (-1); +Derived.prototype.x = (-2); +Derived.prototype[0] = (-3); +Derived.prototype[1] = (-4); +Derived.prototype[2] = (-5); + +for (var i = 0; i < 20; ++i) { + let t = new Derived(); + + for (var j = 0; j < 20; ++j) { + t.do_test_getprop(); + t.do_test_getelem(); + } +} diff --git a/js/src/jit-test/tests/class/superPropMegamorphic.js b/js/src/jit-test/tests/class/superPropMegamorphic.js new file mode 100644 index 0000000000..489f6ececb --- /dev/null +++ b/js/src/jit-test/tests/class/superPropMegamorphic.js @@ -0,0 +1,42 @@ +// Test GETPROP_SUPER with megamorphic variation +const NCLASS = 20; + +var g_prop = "prop"; +var g_THIS = "THIS"; + +// Define array of base classes with a data property and a getter property. +let C = []; +for (let i = 0; i < NCLASS; ++i) { + let klass = class { + get THIS() { return this; } + }; + klass.prototype.prop = i; + + C.push(klass); +} + +// Derive class using super property access +class D extends C[0] { + get prop() { return super.prop; } + get elem() { return super[g_prop]; } + + get prop_this() { return super.THIS; } + get elem_this() { return super[g_THIS]; } +} + +let d = new D(); + +for (var j = 0; j < 4; ++j) { + for (var i = 0; i < 15; ++i) { + // Change base class by overriding [[HomeObject]].[[Prototype]] + Object.setPrototypeOf(D.prototype, C[j].prototype); + + // Check we get property of correct class + assertEq(d.prop, j); + assertEq(d.elem, j); + + // Check super getter gets |this| of object not base class + assertEq(d.prop_this, d); + assertEq(d.elem_this, d); + } +} diff --git a/js/src/jit-test/tests/class/superPropProxy.js b/js/src/jit-test/tests/class/superPropProxy.js new file mode 100644 index 0000000000..fe0e5369a4 --- /dev/null +++ b/js/src/jit-test/tests/class/superPropProxy.js @@ -0,0 +1,23 @@ +// Define constructor with a proxy as prototype +let hook = { get: function(target, name, receiver) { return receiver; } } +let Base = function() { } +Base.prototype = new Proxy(Base.prototype, hook); + +class Derived extends Base { + test() { + // Check proxy receiver is |this|, rather than Base.[[Prototype]] + assertEq(super.x, this); + } + + test_elem() { + // Check proxy receiver is |this|, rather than Base.[[Prototype]] + assertEq(super[0], this); + } +} + +let d = new Derived(); + +for (let i = 0; i < 20; ++i) { + d.test(); + d.test_elem(); +} diff --git a/js/src/jit-test/tests/class/superSetPropThrow.js b/js/src/jit-test/tests/class/superSetPropThrow.js new file mode 100644 index 0000000000..c3b16ac8af --- /dev/null +++ b/js/src/jit-test/tests/class/superSetPropThrow.js @@ -0,0 +1,64 @@ +var g_foo = "foo"; +var g_bar = "bar"; + +// Define base class with a read-only and a writable data property +class Base +{ +} +Object.defineProperty(Base.prototype, "foo", { value: "Base", writable: true }); +Object.defineProperty(Base.prototype, "bar", { value: "Base", writable: false }); + +// Test various cases that should throw during SETPROP_SUPER +class Derived extends Base +{ + // ECMA-2018 9.1.9.1, step 4.a + testReadonly() { + super.bar = "Derived"; + } + testReadonlyElem() { + super[g_bar] = "Derived"; + } + + // ECMA-2018 9.1.9.1, step 4.b + testPrimitiveReceiver() { + super.foo = "Derived"; + } + testPrimitiveReceiverElem() { + super[g_foo] = "Derived"; + } + + // ECMA-2018 9.1.9.1, step 4.d.i + testAccessorShadow() { + Object.defineProperty(this, "foo", { get: function() { } }); + super.foo = "Derived"; + } + testAccessorShadowElem() { + Object.defineProperty(this, "foo", { get: function() { } }); + super[g_foo] = "Derived"; + } + + // ECMA-2018 9.1.9.1, step 4.d.ii + testReadonlyShadow() { + Object.defineProperty(this, "foo", { writable: false }); + super.foo = "Derived"; + } + testReadonlyShadowElem() { + Object.defineProperty(this, "foo", { writable: false }); + super[g_foo] = "Derived"; + } +} + +for (let i = 0; i < 10; ++i) { + var cnt = 0; + + try { new Derived().testReadonly(); } catch(e) { cnt++; } + try { new Derived().testReadonlyElem(); } catch(e) { cnt++; } + try { Derived.prototype.testPrimitiveReceiver.call(null); } catch(e) { cnt++; } + try { Derived.prototype.testPrimitiveReceiverElem.call(null); } catch(e) { cnt++; } + try { new Derived().testAccessorShadow(); } catch(e) { cnt++; } + try { new Derived().testAccessorShadowElem(); } catch(e) { cnt++; } + try { new Derived().testReadonlyShadow(); } catch(e) { cnt++; } + try { new Derived().testReadonlyShadowElem(); } catch(e) { cnt++; } + + assertEq(cnt, 8); +} diff --git a/js/src/jit-test/tests/class/superSetProperty.js b/js/src/jit-test/tests/class/superSetProperty.js new file mode 100644 index 0000000000..6e136cb58b --- /dev/null +++ b/js/src/jit-test/tests/class/superSetProperty.js @@ -0,0 +1,67 @@ + +class Base +{ + set setter(val) { + this.set_val = val; + this.set_this = this; + } +} +Base.prototype.prop = "Base"; + +class Derived extends Base +{ + set setter(val) { super.setter = val; } + setelem(pname, val) { super[pname] = val; } +} + +// Test SETPROP_SUPER invoke setters correctly +function testSetterChain() { + let d = new Derived(); + + for (let i = 0; i < 10; ++i) + { + d.setter = i; + assertEq(d.set_val, i); + assertEq(d.set_this, d); + } +} +function testSetterChainElem() { + let d = new Derived(); + + for (let i = 0; i < 10; ++i) + { + d.setelem("setter", i); + assertEq(d.set_val, i); + assertEq(d.set_this, d); + } +} + +// Test that SETPROP_SUPER modifies |this| and not home object +function testSuperSetProp() { + let d = new Derived(); + + for (let i = 0; i < 10; ++i) + { + d.prop = i; + assertEq(d.prop, i); + assertEq(d.hasOwnProperty("prop"), true); + assertEq(Derived.prototype.prop, "Base"); + } +} +function testSuperSetPropElem() { + let d = new Derived(); + + for (let i = 0; i < 10; ++i) + { + d.setelem("prop", i); + assertEq(d.prop, i); + assertEq(d.hasOwnProperty("prop"), true); + assertEq(Derived.prototype.prop, "Base"); + } +} + +testSetterChain(); +testSetterChainElem(); + +testSuperSetProp(); +testSuperSetPropElem(); diff --git a/js/src/jit-test/tests/class/this-check-after-scalar-replacement-in-derived-class-constructor.js b/js/src/jit-test/tests/class/this-check-after-scalar-replacement-in-derived-class-constructor.js new file mode 100644 index 0000000000..257e723f05 --- /dev/null +++ b/js/src/jit-test/tests/class/this-check-after-scalar-replacement-in-derived-class-constructor.js @@ -0,0 +1,29 @@ +// Create a derived class with a default class constructor. +class C extends class {} {} + +// The default constructor of a derived class is small enough to be inlinable. +assertEq(isSmallFunction(C), true); + +// Bound functions have a configurable "prototype" property. +const BF = function(){}.bind(); + +function testBoundFunction() { + for (let i = 0; i <= 1000; ++i) { + let newTarget = i < 1000 ? C : BF; + Reflect.construct(C, [], newTarget); + } +} + +for (let i = 0; i < 2; ++i) testBoundFunction(); + +// Proxy have a configurable "prototype" property. +const P = new Proxy(function(){}, {}); + +function testProxy() { + for (let i = 0; i <= 1000; ++i) { + let newTarget = i < 1000 ? C : P; + Reflect.construct(C, [], newTarget); + } +} + +for (let i = 0; i < 2; ++i) testProxy(); diff --git a/js/src/jit-test/tests/class/throwOnCallConstructor.js b/js/src/jit-test/tests/class/throwOnCallConstructor.js new file mode 100644 index 0000000000..2bde725702 --- /dev/null +++ b/js/src/jit-test/tests/class/throwOnCallConstructor.js @@ -0,0 +1,77 @@ +// Count constructor calls +var cnt = 0; +class Base { constructor() { ++cnt; } } + +// Force |JSFunction->hasScript()| +new Base(); +assertEq(cnt, 1); + +// Calling a ClassConstructor must throw +(function() { + function f() { Base(); } + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); + +// Spread-calling a ClassConstructor must throw +(function() { + function f() { Base(...[]); } + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); + +// Function.prototype.call must throw on ClassConstructor +(function() { + function f() { Base.call(null); } + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); + +// Function.prototype.apply must throw on ClassConstructor +(function() { + function f() { Base.apply(null, []); } + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); + +// Getter must throw if it is a ClassConstructor +(function() { + var o = {}; + Object.defineProperty(o, "prop", { get: Base }); + function f() { o.prop }; + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); + +// Setter must throw if it is a ClassConstructor +(function() { + var o = {}; + Object.defineProperty(o, "prop", { set: Base }); + function f() { o.prop = 1 }; + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); + +// Proxy apply must throw if it is a ClassConstructor +(function() { + var o = new Proxy(function() { }, { apply: Base }); + function f() { o() }; + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); + +// Proxy get must throw if it is a ClassConstructor +(function() { + var o = new Proxy({}, { get: Base }); + function f() { o.x } + try { f() } catch (e) {} + try { f() } catch (e) {} + assertEq(cnt, 1); +})(); |