[compiler] Omit calling default ctors
I.e., implement the TurboFan handler for the FindNonDefaultConstructor bytecode. Bug: v8:13091 Change-Id: I021b5d24817b47e3ce86cc1ac1377056cfd5e2a5 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3885892 Commit-Queue: Marja Hölttä <marja@chromium.org> Reviewed-by: Jakob Linke <jgruber@chromium.org> Cr-Commit-Position: refs/heads/main@{#83426}
This commit is contained in:
parent
5a7977a3cf
commit
8e72e03d1e
@ -13848,10 +13848,16 @@ void CodeStubAssembler::FindNonDefaultConstructor(
|
||||
GotoIfNot(IsJSFunction(CAST(constructor.value())), found_something_else);
|
||||
|
||||
// If there are class fields, bail out. TODO(v8:13091): Handle them here.
|
||||
TNode<Oddball> has_class_fields =
|
||||
HasProperty(context, constructor.value(), ClassFieldsSymbolConstant(),
|
||||
kHasProperty);
|
||||
GotoIf(IsTrue(has_class_fields), found_something_else);
|
||||
const TNode<SharedFunctionInfo> shared_function_info =
|
||||
LoadObjectField<SharedFunctionInfo>(
|
||||
CAST(constructor.value()), JSFunction::kSharedFunctionInfoOffset);
|
||||
const TNode<Uint32T> has_class_fields =
|
||||
DecodeWord32<SharedFunctionInfo::RequiresInstanceMembersInitializerBit>(
|
||||
LoadObjectField<Uint32T>(shared_function_info,
|
||||
SharedFunctionInfo::kFlagsOffset));
|
||||
|
||||
GotoIf(Word32NotEqual(has_class_fields, Int32Constant(0)),
|
||||
found_something_else);
|
||||
|
||||
// If there are private methods, bail out. TODO(v8:13091): Handle them here.
|
||||
TNode<Context> function_context =
|
||||
|
@ -3230,8 +3230,17 @@ void BytecodeGraphBuilder::VisitGetSuperConstructor() {
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitFindNonDefaultConstructor() {
|
||||
// TODO(v8:13091): Implement.
|
||||
CHECK(false);
|
||||
Node* this_function =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
|
||||
Node* new_target =
|
||||
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
|
||||
|
||||
Node* node = NewNode(javascript()->FindNonDefaultConstructor(), this_function,
|
||||
new_target);
|
||||
|
||||
environment()->BindRegistersToProjections(
|
||||
bytecode_iterator().GetRegisterOperand(2), node,
|
||||
Environment::kAttachFrameState);
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::BuildCompareOp(const Operator* op) {
|
||||
|
@ -1561,6 +1561,7 @@ ObjectRef CallHandlerInfoRef::data() const {
|
||||
HEAP_ACCESSOR_C(ScopeInfo, int, ContextLength)
|
||||
HEAP_ACCESSOR_C(ScopeInfo, bool, HasContextExtensionSlot)
|
||||
HEAP_ACCESSOR_C(ScopeInfo, bool, HasOuterScopeInfo)
|
||||
HEAP_ACCESSOR_C(ScopeInfo, bool, ClassScopeHasPrivateBrand)
|
||||
|
||||
ScopeInfoRef ScopeInfoRef::OuterScopeInfo() const {
|
||||
return MakeRefAssumeMemoryFence(broker(), object()->OuterScopeInfo());
|
||||
@ -1701,7 +1702,7 @@ ZoneVector<const CFunctionInfo*> FunctionTemplateInfoRef::c_signatures() const {
|
||||
|
||||
bool StringRef::IsSeqString() const { return object()->IsSeqString(); }
|
||||
|
||||
ScopeInfoRef NativeContextRef::scope_info() const {
|
||||
ScopeInfoRef ContextRef::scope_info() const {
|
||||
// The scope_info is immutable after initialization.
|
||||
return MakeRefAssumeMemoryFence(broker(), object()->scope_info());
|
||||
}
|
||||
|
@ -527,6 +527,8 @@ class ContextRef : public HeapObjectRef {
|
||||
|
||||
// Only returns a value if the index is valid for this ContextRef.
|
||||
base::Optional<ObjectRef> get(int index) const;
|
||||
|
||||
ScopeInfoRef scope_info() const;
|
||||
};
|
||||
|
||||
#define BROKER_NATIVE_CONTEXT_FIELDS(V) \
|
||||
@ -584,7 +586,6 @@ class NativeContextRef : public ContextRef {
|
||||
BROKER_NATIVE_CONTEXT_FIELDS(DECL_ACCESSOR)
|
||||
#undef DECL_ACCESSOR
|
||||
|
||||
ScopeInfoRef scope_info() const;
|
||||
MapRef GetFunctionMapFromIndex(int index) const;
|
||||
MapRef GetInitialJSArrayMap(ElementsKind kind) const;
|
||||
base::Optional<JSFunctionRef> GetConstructorFunction(const MapRef& map) const;
|
||||
@ -879,6 +880,7 @@ class ScopeInfoRef : public HeapObjectRef {
|
||||
int ContextLength() const;
|
||||
bool HasOuterScopeInfo() const;
|
||||
bool HasContextExtensionSlot() const;
|
||||
bool ClassScopeHasPrivateBrand() const;
|
||||
|
||||
ScopeInfoRef OuterScopeInfo() const;
|
||||
};
|
||||
@ -899,6 +901,7 @@ class ScopeInfoRef : public HeapObjectRef {
|
||||
V(int, StartPosition) \
|
||||
V(bool, is_compiled) \
|
||||
V(bool, IsUserJavaScript) \
|
||||
V(bool, requires_instance_members_initializer) \
|
||||
IF_WASM(V, const wasm::WasmModule*, wasm_module) \
|
||||
IF_WASM(V, const wasm::FunctionSig*, wasm_function_signature)
|
||||
|
||||
|
@ -552,6 +552,10 @@ void JSGenericLowering::LowerJSGetSuperConstructor(Node* node) {
|
||||
AccessBuilder::ForMapPrototype()));
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSFindNonDefaultConstructor(Node* node) {
|
||||
ReplaceWithBuiltinCall(node, Builtin::kFindNonDefaultConstructor);
|
||||
}
|
||||
|
||||
void JSGenericLowering::LowerJSHasInPrototypeChain(Node* node) {
|
||||
ReplaceWithRuntimeCall(node, Runtime::kHasInPrototypeChain);
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
|
||||
return ReduceJSAsyncFunctionResolve(node);
|
||||
case IrOpcode::kJSGetSuperConstructor:
|
||||
return ReduceJSGetSuperConstructor(node);
|
||||
case IrOpcode::kJSFindNonDefaultConstructor:
|
||||
return ReduceJSFindNonDefaultConstructor(node);
|
||||
case IrOpcode::kJSInstanceOf:
|
||||
return ReduceJSInstanceOf(node);
|
||||
case IrOpcode::kJSHasInPrototypeChain:
|
||||
@ -540,6 +542,108 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSFindNonDefaultConstructor(
|
||||
Node* node) {
|
||||
JSFindNonDefaultConstructorNode n(node);
|
||||
Node* this_function = n.this_function();
|
||||
Node* new_target = n.new_target();
|
||||
Node* effect = n.effect();
|
||||
Control control = n.control();
|
||||
|
||||
// TODO(v8:13091): Don't produce incomplete stack traces when debug is active.
|
||||
// We already deopt when a breakpoint is set. But it would be even nicer to
|
||||
// avoid producting incomplete stack traces when when debug is active, even if
|
||||
// there are no breakpoints - then a user inspecting stack traces via Dev
|
||||
// Tools would always see the full stack trace.
|
||||
|
||||
// Check if the input is a known JSFunction.
|
||||
HeapObjectMatcher m(this_function);
|
||||
if (!m.HasResolvedValue() || !m.Ref(broker()).IsJSFunction()) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
JSFunctionRef this_function_ref = m.Ref(broker()).AsJSFunction();
|
||||
MapRef function_map = this_function_ref.map();
|
||||
HeapObjectRef current = function_map.prototype();
|
||||
|
||||
Node* return_value;
|
||||
Node* ctor_or_instance;
|
||||
|
||||
// Walk the class inheritance tree until we find a ctor which is not a default
|
||||
// derived ctor.
|
||||
while (true) {
|
||||
if (!current.IsJSFunction()) {
|
||||
return NoChange();
|
||||
}
|
||||
JSFunctionRef current_function = current.AsJSFunction();
|
||||
|
||||
// If there are class fields, bail out. TODO(v8:13091): Handle them here.
|
||||
if (current_function.shared().requires_instance_members_initializer()) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// If there are private methods, bail out. TODO(v8:13091): Handle them here.
|
||||
if (current_function.context().scope_info().ClassScopeHasPrivateBrand()) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
FunctionKind kind = current_function.shared().kind();
|
||||
|
||||
if (kind != FunctionKind::kDefaultDerivedConstructor) {
|
||||
// The hierarchy walk will end here; this is the last change to bail out
|
||||
// before creating new nodes.
|
||||
if (!dependencies()->DependOnArrayIteratorProtector()) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
if (kind == FunctionKind::kDefaultBaseConstructor) {
|
||||
return_value = jsgraph()->BooleanConstant(true);
|
||||
|
||||
// Generate a builtin call for creating the instance.
|
||||
Node* constructor = jsgraph()->Constant(current_function);
|
||||
|
||||
effect = ctor_or_instance = graph()->NewNode(
|
||||
jsgraph()->javascript()->Create(), constructor, new_target,
|
||||
n.context(), n.frame_state(), effect, control);
|
||||
} else {
|
||||
return_value = jsgraph()->BooleanConstant(false);
|
||||
ctor_or_instance = jsgraph()->Constant(current_function);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Keep walking up the class tree.
|
||||
current = current_function.map().prototype();
|
||||
}
|
||||
|
||||
dependencies()->DependOnStablePrototypeChain(function_map,
|
||||
WhereToStart::kStartAtReceiver);
|
||||
|
||||
// Update the uses of {node}.
|
||||
for (Edge edge : node->use_edges()) {
|
||||
Node* const user = edge.from();
|
||||
if (NodeProperties::IsEffectEdge(edge)) {
|
||||
edge.UpdateTo(effect);
|
||||
} else if (NodeProperties::IsControlEdge(edge)) {
|
||||
edge.UpdateTo(control);
|
||||
} else {
|
||||
DCHECK(NodeProperties::IsValueEdge(edge));
|
||||
switch (ProjectionIndexOf(user->op())) {
|
||||
case 0:
|
||||
Replace(user, return_value);
|
||||
break;
|
||||
case 1:
|
||||
Replace(user, ctor_or_instance);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
node->Kill();
|
||||
return Replace(return_value);
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
|
||||
JSInstanceOfNode n(node);
|
||||
FeedbackParameter const& p = n.Parameters();
|
||||
|
@ -75,6 +75,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
|
||||
Reduction ReduceJSAsyncFunctionReject(Node* node);
|
||||
Reduction ReduceJSAsyncFunctionResolve(Node* node);
|
||||
Reduction ReduceJSGetSuperConstructor(Node* node);
|
||||
Reduction ReduceJSFindNonDefaultConstructor(Node* node);
|
||||
Reduction ReduceJSInstanceOf(Node* node);
|
||||
Reduction ReduceJSHasInPrototypeChain(Node* node);
|
||||
Reduction ReduceJSOrdinaryHasInstance(Node* node);
|
||||
|
@ -770,6 +770,7 @@ Type JSWasmCallNode::TypeForWasmReturnType(const wasm::ValueType& type) {
|
||||
V(RejectPromise, Operator::kNoDeopt | Operator::kNoThrow, 3, 1) \
|
||||
V(ResolvePromise, Operator::kNoDeopt | Operator::kNoThrow, 2, 1) \
|
||||
V(GetSuperConstructor, Operator::kNoWrite | Operator::kNoThrow, 1, 1) \
|
||||
V(FindNonDefaultConstructor, Operator::kNoProperties, 2, 2) \
|
||||
V(ParseInt, Operator::kNoProperties, 2, 1) \
|
||||
V(RegExpTest, Operator::kNoProperties, 2, 1)
|
||||
|
||||
|
@ -1040,6 +1040,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
|
||||
|
||||
const Operator* GetSuperConstructor();
|
||||
|
||||
const Operator* FindNonDefaultConstructor();
|
||||
|
||||
const Operator* CreateGeneratorObject();
|
||||
|
||||
const Operator* LoadGlobal(const NameRef& name,
|
||||
@ -1757,6 +1759,20 @@ class JSForInNextNode final : public JSNodeWrapperBase {
|
||||
#undef INPUTS
|
||||
};
|
||||
|
||||
class JSFindNonDefaultConstructorNode final : public JSNodeWrapperBase {
|
||||
public:
|
||||
explicit constexpr JSFindNonDefaultConstructorNode(Node* node)
|
||||
: JSNodeWrapperBase(node) {
|
||||
DCHECK_EQ(IrOpcode::kJSFindNonDefaultConstructor, node->opcode());
|
||||
}
|
||||
|
||||
#define INPUTS(V) \
|
||||
V(ThisFunction, this_function, 0, Object) \
|
||||
V(NewTarget, new_target, 1, Object)
|
||||
INPUTS(DEFINE_INPUT_ACCESSORS)
|
||||
#undef INPUTS
|
||||
};
|
||||
|
||||
#undef DEFINE_INPUT_ACCESSORS
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -183,7 +183,8 @@
|
||||
V(JSStoreInArrayLiteral) \
|
||||
V(JSDeleteProperty) \
|
||||
V(JSHasProperty) \
|
||||
V(JSGetSuperConstructor)
|
||||
V(JSGetSuperConstructor) \
|
||||
V(JSFindNonDefaultConstructor)
|
||||
|
||||
#define JS_CONTEXT_OP_LIST(V) \
|
||||
V(JSHasContextExtension) \
|
||||
|
@ -91,6 +91,7 @@ bool OperatorProperties::NeedsExactContext(const Operator* op) {
|
||||
case IrOpcode::kJSSetNamedProperty:
|
||||
case IrOpcode::kJSDefineNamedOwnProperty:
|
||||
case IrOpcode::kJSSetKeyedProperty:
|
||||
case IrOpcode::kJSFindNonDefaultConstructor:
|
||||
return true;
|
||||
|
||||
case IrOpcode::kJSAsyncFunctionEnter:
|
||||
@ -239,6 +240,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
|
||||
case IrOpcode::kJSStackCheck:
|
||||
case IrOpcode::kJSDebugger:
|
||||
case IrOpcode::kJSGetSuperConstructor:
|
||||
case IrOpcode::kJSFindNonDefaultConstructor:
|
||||
case IrOpcode::kJSBitwiseNot:
|
||||
case IrOpcode::kJSDecrement:
|
||||
case IrOpcode::kJSIncrement:
|
||||
|
@ -1470,6 +1470,10 @@ Type Typer::Visitor::TypeJSGetSuperConstructor(Node* node) {
|
||||
return Type::NonInternal();
|
||||
}
|
||||
|
||||
Type Typer::Visitor::TypeJSFindNonDefaultConstructor(Node* node) {
|
||||
return Type::Tuple(Type::Boolean(), Type::Object(), zone());
|
||||
}
|
||||
|
||||
// JS context operators.
|
||||
Type Typer::Visitor::TypeJSHasContextExtension(Node* node) {
|
||||
return Type::Boolean();
|
||||
|
@ -1121,6 +1121,13 @@ Type Type::Tuple(Type first, Type second, Type third, Zone* zone) {
|
||||
return FromTypeBase(tuple);
|
||||
}
|
||||
|
||||
Type Type::Tuple(Type first, Type second, Zone* zone) {
|
||||
TupleType* tuple = TupleType::New(2, zone);
|
||||
tuple->InitElement(0, first);
|
||||
tuple->InitElement(1, second);
|
||||
return FromTypeBase(tuple);
|
||||
}
|
||||
|
||||
// static
|
||||
Type Type::OtherNumberConstant(double value, Zone* zone) {
|
||||
return FromTypeBase(OtherNumberConstantType::New(value, zone));
|
||||
|
@ -423,6 +423,7 @@ class V8_EXPORT_PRIVATE Type {
|
||||
static Type Constant(double value, Zone* zone);
|
||||
static Type Range(double min, double max, Zone* zone);
|
||||
static Type Tuple(Type first, Type second, Type third, Zone* zone);
|
||||
static Type Tuple(Type first, Type second, Zone* zone);
|
||||
|
||||
static Type Union(Type type1, Type type2, Zone* zone);
|
||||
static Type Intersect(Type type1, Type type2, Zone* zone);
|
||||
|
@ -790,7 +790,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
CheckValueInputIs(node, 0, Type::Any());
|
||||
CheckTypeIs(node, Type::NonInternal());
|
||||
break;
|
||||
|
||||
case IrOpcode::kJSFindNonDefaultConstructor:
|
||||
CheckValueInputIs(node, 0, Type::Any());
|
||||
CheckValueInputIs(node, 1, Type::Any());
|
||||
break;
|
||||
case IrOpcode::kJSHasContextExtension:
|
||||
CheckTypeIs(node, Type::Boolean());
|
||||
break;
|
||||
|
35
test/mjsunit/compiler/omit-default-ctors-array-iterator.js
Normal file
35
test/mjsunit/compiler/omit-default-ctors-array-iterator.js
Normal file
@ -0,0 +1,35 @@
|
||||
// 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 --allow-natives-syntax --no-maglev --turbofan
|
||||
// Flags: --no-always-turbofan
|
||||
|
||||
// TODO(v8:13091): Enable Maglev.
|
||||
|
||||
// This behavior is not spec compliant, see crbug.com/v8/13249.
|
||||
(function ArrayIteratorMonkeyPatched() {
|
||||
let iterationCount = 0;
|
||||
const oldIterator = Array.prototype[Symbol.iterator];
|
||||
Array.prototype[Symbol.iterator] =
|
||||
function () { ++iterationCount; return oldIterator.call(this); };
|
||||
|
||||
class A {}
|
||||
class B extends A {}
|
||||
class C extends B {}
|
||||
|
||||
%PrepareFunctionForOptimization(C);
|
||||
new C();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
|
||||
// C default ctor doing "...args" and B default ctor doing "...args".
|
||||
assertEquals(2, iterationCount);
|
||||
|
||||
new C();
|
||||
|
||||
// C default ctor doing "...args" and B default ctor doing "...args".
|
||||
assertEquals(4, iterationCount);
|
||||
assertTrue(isTurboFanned(C)); // No deopt.
|
||||
|
||||
Array.prototype[Symbol.iterator] = oldIterator;
|
||||
})();
|
741
test/mjsunit/compiler/omit-default-ctors.js
Normal file
741
test/mjsunit/compiler/omit-default-ctors.js
Normal file
@ -0,0 +1,741 @@
|
||||
// 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 --allow-natives-syntax --no-maglev --turbofan
|
||||
// Flags: --no-always-turbofan
|
||||
|
||||
// TODO(v8:13091): Enable Maglev.
|
||||
|
||||
(function OmitDefaultBaseCtor() {
|
||||
class A {}; // default base ctor -> will be omitted
|
||||
class B extends A {};
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
const o = new B();
|
||||
assertSame(B.prototype, o.__proto__);
|
||||
assertTrue(isTurboFanned(B)); // No deopt.
|
||||
})();
|
||||
|
||||
(function OmitDefaultDerivedCtor() {
|
||||
class A { constructor() {} };
|
||||
class B extends A {}; // default derived ctor -> will be omitted
|
||||
class C extends B {};
|
||||
%PrepareFunctionForOptimization(C);
|
||||
new C();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
const o = new C();
|
||||
assertSame(C.prototype, o.__proto__);
|
||||
assertTrue(isTurboFanned(C)); // No deopt.
|
||||
})();
|
||||
|
||||
(function OmitDefaultBaseAndDerivedCtor() {
|
||||
class A {}; // default base ctor -> will be omitted
|
||||
class B extends A {}; // default derived ctor -> will be omitted
|
||||
class C extends B {};
|
||||
%PrepareFunctionForOptimization(C);
|
||||
new C();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
const o = new C();
|
||||
assertSame(C.prototype, o.__proto__);
|
||||
assertTrue(isTurboFanned(C)); // No deopt.
|
||||
})();
|
||||
|
||||
(function OmitDefaultBaseCtorWithExplicitSuper() {
|
||||
class A {}; // default base ctor -> will be omitted
|
||||
class B extends A { constructor() { super(); } };
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
const o = new B();
|
||||
assertSame(B.prototype, o.__proto__);
|
||||
assertTrue(isTurboFanned(B)); // No deopt.
|
||||
})();
|
||||
|
||||
(function OmitDefaultDerivedCtorWithExplicitSuper() {
|
||||
class A { constructor() {} };
|
||||
class B extends A {}; // default derived ctor -> will be omitted
|
||||
class C extends B { constructor() { super(); } };
|
||||
%PrepareFunctionForOptimization(C);
|
||||
new C();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
const o = new C();
|
||||
assertSame(C.prototype, o.__proto__);
|
||||
assertTrue(isTurboFanned(C)); // No deopt.
|
||||
})();
|
||||
|
||||
(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(); } };
|
||||
%PrepareFunctionForOptimization(C);
|
||||
new C();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
const o = new C();
|
||||
assertSame(C.prototype, o.__proto__);
|
||||
assertTrue(isTurboFanned(C)); // No deopt.
|
||||
})();
|
||||
|
||||
(function OmitDefaultBaseCtorWithExplicitSuperAndNonFinalSpread() {
|
||||
class A {}; // default base ctor -> will be omitted
|
||||
class B extends A { constructor(...args) { super(1, ...args, 2); } };
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
const o = new B(3, 4);
|
||||
assertSame(B.prototype, o.__proto__);
|
||||
// See https://bugs.chromium.org/p/v8/issues/detail?id=13310
|
||||
// assertTrue(isTurboFanned(B)); // No deopt.
|
||||
// This assert will fail when the above bug is fixed:
|
||||
assertFalse(isTurboFanned(B));
|
||||
})();
|
||||
|
||||
(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); } };
|
||||
%PrepareFunctionForOptimization(C);
|
||||
new C();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
const o = new C(3, 4);
|
||||
assertSame(C.prototype, o.__proto__);
|
||||
// See https://bugs.chromium.org/p/v8/issues/detail?id=13310
|
||||
// assertTrue(isTurboFanned(C)); // No deopt.
|
||||
// This assert will fail when the above bug is fixed:
|
||||
assertFalse(isTurboFanned(C));
|
||||
})();
|
||||
|
||||
(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); } };
|
||||
%PrepareFunctionForOptimization(C);
|
||||
new C();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
const o = new C(3, 4);
|
||||
assertSame(C.prototype, o.__proto__);
|
||||
// See https://bugs.chromium.org/p/v8/issues/detail?id=13310
|
||||
// assertTrue(isTurboFanned(C)); // No deopt.
|
||||
// This assert will fail when the above bug is fixed:
|
||||
assertFalse(isTurboFanned(C));
|
||||
})();
|
||||
|
||||
(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 {};
|
||||
%PrepareFunctionForOptimization(A);
|
||||
new A();
|
||||
%OptimizeFunctionOnNextCall(A);
|
||||
const a = new A(1, 2, 3);
|
||||
assertEquals(2, ctorCallCount);
|
||||
assertEquals([1, 2, 3], lastArgs);
|
||||
assertTrue(a.baseTagged);
|
||||
assertTrue(isTurboFanned(A)); // No deopt.
|
||||
|
||||
// 'A' default ctor will be omitted.
|
||||
class B1 extends A {};
|
||||
%PrepareFunctionForOptimization(B1);
|
||||
new B1();
|
||||
%OptimizeFunctionOnNextCall(B1);
|
||||
const b1 = new B1(4, 5, 6);
|
||||
assertEquals(4, ctorCallCount);
|
||||
assertEquals([4, 5, 6], lastArgs);
|
||||
assertTrue(b1.baseTagged);
|
||||
assertTrue(isTurboFanned(B1)); // No deopt.
|
||||
|
||||
// The same test with non-final spread; 'A' default ctor will be omitted.
|
||||
class B2 extends A {
|
||||
constructor(...args) { super(1, ...args, 2); }
|
||||
};
|
||||
%PrepareFunctionForOptimization(B2);
|
||||
new B2();
|
||||
%OptimizeFunctionOnNextCall(B2);
|
||||
const b2 = new B2(4, 5, 6);
|
||||
assertEquals(6, ctorCallCount);
|
||||
assertEquals([1, 4, 5, 6, 2], lastArgs);
|
||||
assertTrue(b2.baseTagged);
|
||||
// See https://bugs.chromium.org/p/v8/issues/detail?id=13310
|
||||
// assertTrue(isTurboFanned(B2)); // No deopt.
|
||||
// This assert will fail when the above bug is fixed:
|
||||
assertFalse(isTurboFanned(B2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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 {};
|
||||
%PrepareFunctionForOptimization(A);
|
||||
new A();
|
||||
%OptimizeFunctionOnNextCall(A);
|
||||
const a = new A(1, 2, 3);
|
||||
assertEquals(2, ctorCallCount);
|
||||
assertEquals([1, 2, 3], lastArgs);
|
||||
assertTrue(a.derivedTagged);
|
||||
assertTrue(isTurboFanned(A)); // No deopt.
|
||||
|
||||
// 'A' default ctor will be omitted.
|
||||
class B1 extends A {};
|
||||
%PrepareFunctionForOptimization(B1);
|
||||
new B1();
|
||||
%OptimizeFunctionOnNextCall(B1);
|
||||
const b1 = new B1(4, 5, 6);
|
||||
assertEquals(4, ctorCallCount);
|
||||
assertEquals([4, 5, 6], lastArgs);
|
||||
assertTrue(b1.derivedTagged);
|
||||
assertTrue(isTurboFanned(B1)); // No deopt.
|
||||
|
||||
// The same test with non-final spread. 'A' default ctor will be omitted.
|
||||
class B2 extends A {
|
||||
constructor(...args) { super(1, ...args, 2); }
|
||||
};
|
||||
%PrepareFunctionForOptimization(B2);
|
||||
new B2();
|
||||
%OptimizeFunctionOnNextCall(B2);
|
||||
const b2 = new B2(4, 5, 6);
|
||||
assertEquals(6, ctorCallCount);
|
||||
assertEquals([1, 4, 5, 6, 2], lastArgs);
|
||||
assertTrue(b2.derivedTagged);
|
||||
// See https://bugs.chromium.org/p/v8/issues/detail?id=13310
|
||||
// assertTrue(isTurboFanned(B2)); // No deopt.
|
||||
// This assert will fail when the above bug is fixed:
|
||||
assertFalse(isTurboFanned(B2)); // No deopt.
|
||||
})();
|
||||
|
||||
(function BaseFunctionCalled() {
|
||||
let baseFunctionCallCount = 0;
|
||||
function BaseFunction() {
|
||||
++baseFunctionCallCount;
|
||||
this.baseTagged = true;
|
||||
}
|
||||
|
||||
class A1 extends BaseFunction {};
|
||||
%PrepareFunctionForOptimization(A1);
|
||||
new A1();
|
||||
%OptimizeFunctionOnNextCall(A1);
|
||||
const a1 = new A1();
|
||||
assertEquals(2, baseFunctionCallCount);
|
||||
assertTrue(a1.baseTagged);
|
||||
assertTrue(isTurboFanned(A1)); // No deopt.
|
||||
|
||||
class A2 extends BaseFunction {
|
||||
constructor(...args) { super(1, ...args, 2); }
|
||||
};
|
||||
%PrepareFunctionForOptimization(A2);
|
||||
new A2();
|
||||
%OptimizeFunctionOnNextCall(A2);
|
||||
const a2 = new A2();
|
||||
assertEquals(4, baseFunctionCallCount);
|
||||
assertTrue(a2.baseTagged);
|
||||
assertTrue(isTurboFanned(A2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(C);
|
||||
%PrepareFunctionForOptimization(D1);
|
||||
%PrepareFunctionForOptimization(D2);
|
||||
new C();
|
||||
new D1();
|
||||
new D2();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
%OptimizeFunctionOnNextCall(D1);
|
||||
%OptimizeFunctionOnNextCall(D2);
|
||||
|
||||
// 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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(C);
|
||||
%PrepareFunctionForOptimization(D1);
|
||||
%PrepareFunctionForOptimization(D2);
|
||||
new C();
|
||||
new D1();
|
||||
new D2();
|
||||
%OptimizeFunctionOnNextCall(C);
|
||||
%OptimizeFunctionOnNextCall(D1);
|
||||
%OptimizeFunctionOnNextCall(D2);
|
||||
|
||||
// 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());
|
||||
}
|
||||
};
|
||||
|
||||
let callCount = 0;
|
||||
function foo() {
|
||||
++callCount;
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(D1);
|
||||
%PrepareFunctionForOptimization(D2);
|
||||
new D1();
|
||||
new D2();
|
||||
%OptimizeFunctionOnNextCall(D1);
|
||||
%OptimizeFunctionOnNextCall(D2);
|
||||
assertEquals(2, callCount);
|
||||
|
||||
// Install an object which is not a constructor into the class hierarchy.
|
||||
C.__proto__ = {};
|
||||
|
||||
assertThrows(() => { new D1(); }, TypeError);
|
||||
assertEquals(3, callCount);
|
||||
|
||||
assertThrows(() => { new D2(); }, TypeError);
|
||||
assertEquals(4, 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;
|
||||
let changeHierarchy = false;
|
||||
function foo() {
|
||||
if (changeHierarchy) {
|
||||
C.__proto__ = A;
|
||||
C.prototype.__proto__ = A.prototype;
|
||||
}
|
||||
++fooCallCount;
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(D);
|
||||
new D();
|
||||
assertEquals(1, fooCallCount);
|
||||
assertEquals(1, ctorCallCount);
|
||||
%OptimizeFunctionOnNextCall(D);
|
||||
changeHierarchy = true;
|
||||
|
||||
new D();
|
||||
assertEquals(2, fooCallCount);
|
||||
assertEquals(1, ctorCallCount);
|
||||
assertFalse(isTurboFanned(D)); // Deopt.
|
||||
})();
|
||||
|
||||
// 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;
|
||||
let changeHierarchy = false;
|
||||
function foo() {
|
||||
if (changeHierarchy) {
|
||||
C.__proto__ = A;
|
||||
C.prototype.__proto__ = A.prototype;
|
||||
}
|
||||
++fooCallCount;
|
||||
}
|
||||
|
||||
%PrepareFunctionForOptimization(D);
|
||||
new D();
|
||||
assertEquals(1, fooCallCount);
|
||||
assertEquals(1, ctorCallCount);
|
||||
%OptimizeFunctionOnNextCall(D);
|
||||
changeHierarchy = true;
|
||||
|
||||
new D();
|
||||
assertEquals(2, fooCallCount);
|
||||
assertEquals(1, ctorCallCount);
|
||||
assertFalse(isTurboFanned(D)); // Deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
|
||||
const b = new B();
|
||||
assertTrue(b.isA());
|
||||
assertTrue(isTurboFanned(B)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertTrue(c1.isA());
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertTrue(c2.isA());
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertTrue(c1.isB());
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertTrue(c2.isB());
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
|
||||
const b = new B();
|
||||
assertEquals('private', b.callPrivate());
|
||||
assertTrue(isTurboFanned(B)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertEquals('private', c1.callPrivate());
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertEquals('private', c2.callPrivate());
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertEquals('private', c1.callPrivate());
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertEquals('private', c2.callPrivate());
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
|
||||
const b = new B();
|
||||
assertEquals('private', b.getPrivate());
|
||||
assertTrue(isTurboFanned(B)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertEquals('private', c1.getPrivate());
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertEquals('private', c2.getPrivate());
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertEquals('private', c1.getPrivate());
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertEquals('private', c2.getPrivate());
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
|
||||
const b = new B();
|
||||
b.setPrivate();
|
||||
assertEquals('private', b.secret);
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
c1.setPrivate();
|
||||
assertEquals('private', c1.secret);
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
c2.setPrivate();
|
||||
assertEquals('private', c2.secret);
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(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); }};
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
c1.setPrivate();
|
||||
assertEquals('private', c1.secret);
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
c2.setPrivate();
|
||||
assertEquals('private', c2.secret);
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(function BaseClassFields() {
|
||||
class A {
|
||||
aField = true;
|
||||
};
|
||||
class B extends A {};
|
||||
class C1 extends B {};
|
||||
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
||||
|
||||
%PrepareFunctionForOptimization(B);
|
||||
new B();
|
||||
%OptimizeFunctionOnNextCall(B);
|
||||
|
||||
const b = new B();
|
||||
assertTrue(b.aField);
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertTrue(c1.aField);
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertTrue(c2.aField);
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
||||
|
||||
(function DerivedClassFields() {
|
||||
class A {};
|
||||
class B extends A {
|
||||
bField = true;
|
||||
};
|
||||
class C1 extends B {};
|
||||
class C2 extends B { constructor(...args) { super(1, ...args, 2); }};
|
||||
|
||||
%PrepareFunctionForOptimization(C1);
|
||||
new C1();
|
||||
%OptimizeFunctionOnNextCall(C1);
|
||||
|
||||
const c1 = new C1();
|
||||
assertTrue(c1.bField);
|
||||
assertTrue(isTurboFanned(C1)); // No deopt.
|
||||
|
||||
%PrepareFunctionForOptimization(C2);
|
||||
new C2();
|
||||
%OptimizeFunctionOnNextCall(C2);
|
||||
|
||||
const c2 = new C2();
|
||||
assertTrue(c2.bField);
|
||||
assertTrue(isTurboFanned(C2)); // No deopt.
|
||||
})();
|
@ -2,9 +2,8 @@
|
||||
// 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 --no-maglev
|
||||
// Flags: --omit-default-ctors --no-maglev
|
||||
|
||||
// TODO(v8:13091): Enable TurboFan.
|
||||
// TODO(v8:13091): Enable Maglev.
|
||||
|
||||
// This behavior is not spec compliant, see crbug.com/v8/13249.
|
||||
|
@ -2,9 +2,8 @@
|
||||
// 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 --no-maglev
|
||||
// Flags: --omit-default-ctors --no-maglev
|
||||
|
||||
// TODO(v8:13091): Enable TurboFan.
|
||||
// TODO(v8:13091): Enable Maglev.
|
||||
|
||||
(function OmitDefaultBaseCtor() {
|
||||
|
Loading…
Reference in New Issue
Block a user