c7bf46eabc
I.e., implement a baseline handler for the FindNonDefaultConstructor bytecode. Bug: v8:13091 Change-Id: If1b119ae0479e54d2a89143bf8f40faeadb1abaf Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3871206 Reviewed-by: Leszek Swirski <leszeks@chromium.org> Commit-Queue: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/main@{#83076}
485 lines
12 KiB
JavaScript
485 lines
12 KiB
JavaScript
// Copyright 2022 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flags: --omit-default-ctors --no-turbofan --no-always-turbofan
|
|
|
|
// TODO(v8:13091): Enable TurboFan.
|
|
|
|
(function OmitDefaultBaseCtor() {
|
|
class A {} // default base ctor -> will be omitted
|
|
class B extends A {}
|
|
const o = new B();
|
|
assertSame(B.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultDerivedCtor() {
|
|
class A { constructor() {} }
|
|
class B extends A {} // default derived ctor -> will be omitted
|
|
class C extends B {}
|
|
const o = new C();
|
|
assertSame(C.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultBaseAndDerivedCtor() {
|
|
class A {} // default base ctor -> will be omitted
|
|
class B extends A {} // default derived ctor -> will be omitted
|
|
class C extends B {}
|
|
const o = new C();
|
|
assertSame(C.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultBaseCtorWithExplicitSuper() {
|
|
class A {} // default base ctor -> will be omitted
|
|
class B extends A { constructor() { super(); } }
|
|
const o = new B();
|
|
assertSame(B.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultDerivedCtorWithExplicitSuper() {
|
|
class A { constructor() {} }
|
|
class B extends A {} // default derived ctor -> will be omitted
|
|
class C extends B { constructor() { super(); } }
|
|
const o = new C();
|
|
assertSame(C.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultBaseAndDerivedCtorWithExplicitSuper() {
|
|
class A {} // default base ctor -> will be omitted
|
|
class B extends A {} // default derived ctor -> will be omitted
|
|
class C extends B { constructor() { super(); } }
|
|
const o = new C();
|
|
assertSame(C.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultBaseCtorWithExplicitSuperAndNonFinalSpread() {
|
|
class A {} // default base ctor -> will be omitted
|
|
class B extends A { constructor(...args) { super(1, ...args, 2); } }
|
|
const o = new B(3, 4);
|
|
assertSame(B.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultDerivedCtorWithExplicitSuperAndNonFinalSpread() {
|
|
class A { constructor() {} }
|
|
class B extends A {} // default derived ctor -> will be omitted
|
|
class C extends B { constructor(...args) { super(1, ...args, 2); } }
|
|
const o = new C(3, 4);
|
|
assertSame(C.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function OmitDefaultBaseAndDerivedCtorWithExplicitSuperAndNonFinalSpread() {
|
|
class A {} // default base ctor -> will be omitted
|
|
class B extends A {} // default derived ctor -> will be omitted
|
|
class C extends B { constructor(...args) { super(1, ...args, 2); } }
|
|
const o = new C(3, 4);
|
|
assertSame(C.prototype, o.__proto__);
|
|
})();
|
|
|
|
(function NonDefaultBaseConstructorCalled() {
|
|
let ctorCallCount = 0;
|
|
let lastArgs;
|
|
class Base {
|
|
constructor(...args) {
|
|
++ctorCallCount;
|
|
this.baseTagged = true;
|
|
lastArgs = args;
|
|
}
|
|
}
|
|
// Nothing will be omitted.
|
|
class A extends Base {}
|
|
const a = new A(1, 2, 3);
|
|
assertEquals(1, ctorCallCount);
|
|
assertEquals([1, 2, 3], lastArgs);
|
|
assertTrue(a.baseTagged);
|
|
|
|
// 'A' default ctor will be omitted.
|
|
class B1 extends A {}
|
|
const b1 = new B1(4, 5, 6);
|
|
assertEquals(2, ctorCallCount);
|
|
assertEquals([4, 5, 6], lastArgs);
|
|
assertTrue(b1.baseTagged);
|
|
|
|
// The same test with non-final spread; 'A' default ctor will be omitted.
|
|
class B2 extends A {
|
|
constructor(...args) { super(1, ...args, 2); }
|
|
}
|
|
const b2 = new B2(4, 5, 6);
|
|
assertEquals(3, ctorCallCount);
|
|
assertEquals([1, 4, 5, 6, 2], lastArgs);
|
|
assertTrue(b2.baseTagged);
|
|
})();
|
|
|
|
(function NonDefaultDerivedConstructorCalled() {
|
|
let ctorCallCount = 0;
|
|
let lastArgs;
|
|
class Base {}
|
|
class Derived extends Base {
|
|
constructor(...args) {
|
|
super();
|
|
++ctorCallCount;
|
|
this.derivedTagged = true;
|
|
lastArgs = args;
|
|
}
|
|
}
|
|
// Nothing will be omitted.
|
|
class A extends Derived {}
|
|
const a = new A(1, 2, 3);
|
|
assertEquals(1, ctorCallCount);
|
|
assertEquals([1, 2, 3], lastArgs);
|
|
assertTrue(a.derivedTagged);
|
|
|
|
// 'A' default ctor will be omitted.
|
|
class B1 extends A {}
|
|
const b1 = new B1(4, 5, 6);
|
|
assertEquals(2, ctorCallCount);
|
|
assertEquals([4, 5, 6], lastArgs);
|
|
assertTrue(b1.derivedTagged);
|
|
|
|
// The same test with non-final spread. 'A' default ctor will be omitted.
|
|
class B2 extends A {
|
|
constructor(...args) { super(1, ...args, 2); }
|
|
}
|
|
const b2 = new B2(4, 5, 6);
|
|
assertEquals(3, ctorCallCount);
|
|
assertEquals([1, 4, 5, 6, 2], lastArgs);
|
|
assertTrue(b2.derivedTagged);
|
|
})();
|
|
|
|
(function BaseFunctionCalled() {
|
|
let baseFunctionCallCount = 0;
|
|
function BaseFunction() {
|
|
++baseFunctionCallCount;
|
|
this.baseTagged = true;
|
|
}
|
|
|
|
class A1 extends BaseFunction {}
|
|
const a1 = new A1();
|
|
assertEquals(1, baseFunctionCallCount);
|
|
assertTrue(a1.baseTagged);
|
|
|
|
class A2 extends BaseFunction {
|
|
constructor(...args) { super(1, ...args, 2); }
|
|
}
|
|
const a2 = new A2();
|
|
assertEquals(2, baseFunctionCallCount);
|
|
assertTrue(a2.baseTagged);
|
|
})();
|
|
|
|
(function NonSuperclassCtor() {
|
|
class A {};
|
|
class B extends A {};
|
|
class C extends B {};
|
|
class D1 extends C {};
|
|
class D2 extends C { constructor(...args) { super(1, ...args, 2); }}
|
|
|
|
// Install an object which is not a constructor into the class hierarchy.
|
|
C.__proto__ = {};
|
|
|
|
assertThrows(() => { new C(); }, TypeError);
|
|
assertThrows(() => { new D1(); }, TypeError);
|
|
assertThrows(() => { new D2(); }, TypeError);
|
|
})();
|
|
|
|
(function ArgumentsEvaluatedBeforeNonSuperclassCtorDetected() {
|
|
class A {};
|
|
class B extends A {};
|
|
class C extends B {};
|
|
class D1 extends C {};
|
|
class D2 extends C { constructor(...args) { super(1, ...args, 2); }}
|
|
|
|
// Install an object which is not a constructor into the class hierarchy.
|
|
C.__proto__ = {};
|
|
|
|
let callCount = 0;
|
|
function foo() {
|
|
++callCount;
|
|
}
|
|
|
|
assertThrows(() => { new C(foo()); }, TypeError);
|
|
assertEquals(1, callCount);
|
|
|
|
assertThrows(() => { new D1(foo()); }, TypeError);
|
|
assertEquals(2, callCount);
|
|
|
|
assertThrows(() => { new D2(foo()); }, TypeError);
|
|
assertEquals(3, callCount);
|
|
})();
|
|
|
|
(function ArgumentsEvaluatedBeforeNonSuperclassCtorDetected2() {
|
|
class A {};
|
|
class B extends A {};
|
|
class C extends B {};
|
|
class D1 extends C {
|
|
constructor() {
|
|
super(foo());
|
|
}
|
|
};
|
|
|
|
class D2 extends C {
|
|
constructor(...args) {
|
|
super(...args, foo());
|
|
}
|
|
};
|
|
|
|
// Install an object which is not a constructor into the class hierarchy.
|
|
C.__proto__ = {};
|
|
|
|
let callCount = 0;
|
|
function foo() {
|
|
++callCount;
|
|
}
|
|
|
|
assertThrows(() => { new D1(); }, TypeError);
|
|
assertEquals(1, callCount);
|
|
|
|
assertThrows(() => { new D2(); }, TypeError);
|
|
assertEquals(2, callCount);
|
|
})();
|
|
|
|
(function EvaluatingArgumentsChangesClassHierarchy() {
|
|
let ctorCallCount = 0;
|
|
class A {};
|
|
class B extends A { constructor() {
|
|
super();
|
|
++ctorCallCount;
|
|
}};
|
|
class C extends B {};
|
|
class D extends C {
|
|
constructor() {
|
|
super(foo());
|
|
}
|
|
};
|
|
|
|
let fooCallCount = 0;
|
|
function foo() {
|
|
C.__proto__ = A;
|
|
C.prototype.__proto__ = A.prototype;
|
|
++fooCallCount;
|
|
}
|
|
|
|
new D();
|
|
assertEquals(1, fooCallCount);
|
|
assertEquals(0, ctorCallCount);
|
|
})();
|
|
|
|
// The same test as the previous one, but with a ctor with a non-final spread.
|
|
(function EvaluatingArgumentsChangesClassHierarchyThisTimeWithNonFinalSpread() {
|
|
let ctorCallCount = 0;
|
|
class A {};
|
|
class B extends A { constructor() {
|
|
super();
|
|
++ctorCallCount;
|
|
}};
|
|
class C extends B {};
|
|
class D extends C {
|
|
constructor(...args) {
|
|
super(...args, foo());
|
|
}
|
|
};
|
|
|
|
let fooCallCount = 0;
|
|
function foo() {
|
|
C.__proto__ = A;
|
|
C.prototype.__proto__ = A.prototype;
|
|
++fooCallCount;
|
|
}
|
|
|
|
new D();
|
|
assertEquals(1, fooCallCount);
|
|
assertEquals(0, ctorCallCount);
|
|
})();
|
|
|
|
(function BasePrivateField() {
|
|
class A {
|
|
#aBrand = true;
|
|
isA() {
|
|
return #aBrand in this;
|
|
}
|
|
};
|
|
class B extends A {};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const b = new B();
|
|
assertTrue(b.isA());
|
|
|
|
const c1 = new C1();
|
|
assertTrue(c1.isA());
|
|
|
|
const c2 = new C2();
|
|
assertTrue(c2.isA());
|
|
})();
|
|
|
|
(function DerivedPrivateField() {
|
|
class A {};
|
|
class B extends A {
|
|
#bBrand = true;
|
|
isB() {
|
|
return #bBrand in this;
|
|
}
|
|
};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const c1 = new C1();
|
|
assertTrue(c1.isB());
|
|
|
|
const c2 = new C2();
|
|
assertTrue(c2.isB());
|
|
})();
|
|
|
|
(function BasePrivateMethod() {
|
|
class A {
|
|
#m() { return 'private'; }
|
|
callPrivate() {
|
|
return this.#m();
|
|
}
|
|
};
|
|
class B extends A {};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const b = new B();
|
|
assertEquals('private', b.callPrivate());
|
|
|
|
const c1 = new C1();
|
|
assertEquals('private', c1.callPrivate());
|
|
|
|
const c2 = new C2();
|
|
assertEquals('private', c2.callPrivate());
|
|
})();
|
|
|
|
(function DerivedPrivateMethod() {
|
|
class A {};
|
|
class B extends A {
|
|
#m() { return 'private'; }
|
|
callPrivate() {
|
|
return this.#m();
|
|
}
|
|
};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const c1 = new C1();
|
|
assertEquals('private', c1.callPrivate());
|
|
|
|
const c2 = new C2();
|
|
assertEquals('private', c2.callPrivate());
|
|
})();
|
|
|
|
(function BasePrivateGetter() {
|
|
class A {
|
|
get #p() { return 'private'; }
|
|
getPrivate() {
|
|
return this.#p;
|
|
}
|
|
};
|
|
class B extends A {};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const b = new B();
|
|
assertEquals('private', b.getPrivate());
|
|
|
|
const c1 = new C1();
|
|
assertEquals('private', c1.getPrivate());
|
|
|
|
const c2 = new C2();
|
|
assertEquals('private', c2.getPrivate());
|
|
})();
|
|
|
|
(function DerivedPrivateGetter() {
|
|
class A {};
|
|
class B extends A {
|
|
get #p() { return 'private'; }
|
|
getPrivate() {
|
|
return this.#p;
|
|
}
|
|
};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const c1 = new C1();
|
|
assertEquals('private', c1.getPrivate());
|
|
|
|
const c2 = new C2();
|
|
assertEquals('private', c2.getPrivate());
|
|
})();
|
|
|
|
(function BasePrivateSetter() {
|
|
class A {
|
|
set #p(value) { this.secret = value; }
|
|
setPrivate() {
|
|
this.#p = 'private';
|
|
}
|
|
};
|
|
class B extends A {};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const b = new B();
|
|
b.setPrivate();
|
|
assertEquals('private', b.secret);
|
|
|
|
const c1 = new C1();
|
|
c1.setPrivate();
|
|
assertEquals('private', c1.secret);
|
|
|
|
const c2 = new C2();
|
|
c2.setPrivate();
|
|
assertEquals('private', c2.secret);
|
|
})();
|
|
|
|
(function DerivedPrivateSetter() {
|
|
class A {};
|
|
class B extends A {
|
|
set #p(value) { this.secret = value; }
|
|
setPrivate() {
|
|
this.#p = 'private';
|
|
}
|
|
};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const c1 = new C1();
|
|
c1.setPrivate();
|
|
assertEquals('private', c1.secret);
|
|
|
|
const c2 = new C2();
|
|
c2.setPrivate();
|
|
assertEquals('private', c2.secret);
|
|
})();
|
|
|
|
(function BaseClassFields() {
|
|
class A {
|
|
aField = true;
|
|
};
|
|
class B extends A {};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const b = new B();
|
|
assertTrue(b.aField);
|
|
|
|
const c1 = new C1();
|
|
assertTrue(c1.aField);
|
|
|
|
const c2 = new C2();
|
|
assertTrue(c2.aField);
|
|
})();
|
|
|
|
(function DerivedClassFields() {
|
|
class A {};
|
|
class B extends A {
|
|
bField = true;
|
|
};
|
|
class C1 extends B {};
|
|
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
|
|
|
const c1 = new C1();
|
|
assertTrue(c1.bField);
|
|
|
|
const c2 = new C2();
|
|
assertTrue(c2.bField);
|
|
})();
|