Commit 8e72e03d authored by Marja Hölttä's avatar Marja Hölttä Committed by V8 LUCI CQ

[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: 's avatarJakob Linke <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83426}
parent 5a7977a3
...@@ -13848,10 +13848,16 @@ void CodeStubAssembler::FindNonDefaultConstructor( ...@@ -13848,10 +13848,16 @@ void CodeStubAssembler::FindNonDefaultConstructor(
GotoIfNot(IsJSFunction(CAST(constructor.value())), found_something_else); GotoIfNot(IsJSFunction(CAST(constructor.value())), found_something_else);
// If there are class fields, bail out. TODO(v8:13091): Handle them here. // If there are class fields, bail out. TODO(v8:13091): Handle them here.
TNode<Oddball> has_class_fields = const TNode<SharedFunctionInfo> shared_function_info =
HasProperty(context, constructor.value(), ClassFieldsSymbolConstant(), LoadObjectField<SharedFunctionInfo>(
kHasProperty); CAST(constructor.value()), JSFunction::kSharedFunctionInfoOffset);
GotoIf(IsTrue(has_class_fields), found_something_else); 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. // If there are private methods, bail out. TODO(v8:13091): Handle them here.
TNode<Context> function_context = TNode<Context> function_context =
......
...@@ -3230,8 +3230,17 @@ void BytecodeGraphBuilder::VisitGetSuperConstructor() { ...@@ -3230,8 +3230,17 @@ void BytecodeGraphBuilder::VisitGetSuperConstructor() {
} }
void BytecodeGraphBuilder::VisitFindNonDefaultConstructor() { void BytecodeGraphBuilder::VisitFindNonDefaultConstructor() {
// TODO(v8:13091): Implement. Node* this_function =
CHECK(false); 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) { void BytecodeGraphBuilder::BuildCompareOp(const Operator* op) {
......
...@@ -1561,6 +1561,7 @@ ObjectRef CallHandlerInfoRef::data() const { ...@@ -1561,6 +1561,7 @@ ObjectRef CallHandlerInfoRef::data() const {
HEAP_ACCESSOR_C(ScopeInfo, int, ContextLength) HEAP_ACCESSOR_C(ScopeInfo, int, ContextLength)
HEAP_ACCESSOR_C(ScopeInfo, bool, HasContextExtensionSlot) HEAP_ACCESSOR_C(ScopeInfo, bool, HasContextExtensionSlot)
HEAP_ACCESSOR_C(ScopeInfo, bool, HasOuterScopeInfo) HEAP_ACCESSOR_C(ScopeInfo, bool, HasOuterScopeInfo)
HEAP_ACCESSOR_C(ScopeInfo, bool, ClassScopeHasPrivateBrand)
ScopeInfoRef ScopeInfoRef::OuterScopeInfo() const { ScopeInfoRef ScopeInfoRef::OuterScopeInfo() const {
return MakeRefAssumeMemoryFence(broker(), object()->OuterScopeInfo()); return MakeRefAssumeMemoryFence(broker(), object()->OuterScopeInfo());
...@@ -1701,7 +1702,7 @@ ZoneVector<const CFunctionInfo*> FunctionTemplateInfoRef::c_signatures() const { ...@@ -1701,7 +1702,7 @@ ZoneVector<const CFunctionInfo*> FunctionTemplateInfoRef::c_signatures() const {
bool StringRef::IsSeqString() const { return object()->IsSeqString(); } bool StringRef::IsSeqString() const { return object()->IsSeqString(); }
ScopeInfoRef NativeContextRef::scope_info() const { ScopeInfoRef ContextRef::scope_info() const {
// The scope_info is immutable after initialization. // The scope_info is immutable after initialization.
return MakeRefAssumeMemoryFence(broker(), object()->scope_info()); return MakeRefAssumeMemoryFence(broker(), object()->scope_info());
} }
......
...@@ -527,6 +527,8 @@ class ContextRef : public HeapObjectRef { ...@@ -527,6 +527,8 @@ class ContextRef : public HeapObjectRef {
// Only returns a value if the index is valid for this ContextRef. // Only returns a value if the index is valid for this ContextRef.
base::Optional<ObjectRef> get(int index) const; base::Optional<ObjectRef> get(int index) const;
ScopeInfoRef scope_info() const;
}; };
#define BROKER_NATIVE_CONTEXT_FIELDS(V) \ #define BROKER_NATIVE_CONTEXT_FIELDS(V) \
...@@ -584,7 +586,6 @@ class NativeContextRef : public ContextRef { ...@@ -584,7 +586,6 @@ class NativeContextRef : public ContextRef {
BROKER_NATIVE_CONTEXT_FIELDS(DECL_ACCESSOR) BROKER_NATIVE_CONTEXT_FIELDS(DECL_ACCESSOR)
#undef DECL_ACCESSOR #undef DECL_ACCESSOR
ScopeInfoRef scope_info() const;
MapRef GetFunctionMapFromIndex(int index) const; MapRef GetFunctionMapFromIndex(int index) const;
MapRef GetInitialJSArrayMap(ElementsKind kind) const; MapRef GetInitialJSArrayMap(ElementsKind kind) const;
base::Optional<JSFunctionRef> GetConstructorFunction(const MapRef& map) const; base::Optional<JSFunctionRef> GetConstructorFunction(const MapRef& map) const;
...@@ -879,6 +880,7 @@ class ScopeInfoRef : public HeapObjectRef { ...@@ -879,6 +880,7 @@ class ScopeInfoRef : public HeapObjectRef {
int ContextLength() const; int ContextLength() const;
bool HasOuterScopeInfo() const; bool HasOuterScopeInfo() const;
bool HasContextExtensionSlot() const; bool HasContextExtensionSlot() const;
bool ClassScopeHasPrivateBrand() const;
ScopeInfoRef OuterScopeInfo() const; ScopeInfoRef OuterScopeInfo() const;
}; };
...@@ -899,6 +901,7 @@ class ScopeInfoRef : public HeapObjectRef { ...@@ -899,6 +901,7 @@ class ScopeInfoRef : public HeapObjectRef {
V(int, StartPosition) \ V(int, StartPosition) \
V(bool, is_compiled) \ V(bool, is_compiled) \
V(bool, IsUserJavaScript) \ V(bool, IsUserJavaScript) \
V(bool, requires_instance_members_initializer) \
IF_WASM(V, const wasm::WasmModule*, wasm_module) \ IF_WASM(V, const wasm::WasmModule*, wasm_module) \
IF_WASM(V, const wasm::FunctionSig*, wasm_function_signature) IF_WASM(V, const wasm::FunctionSig*, wasm_function_signature)
......
...@@ -552,6 +552,10 @@ void JSGenericLowering::LowerJSGetSuperConstructor(Node* node) { ...@@ -552,6 +552,10 @@ void JSGenericLowering::LowerJSGetSuperConstructor(Node* node) {
AccessBuilder::ForMapPrototype())); AccessBuilder::ForMapPrototype()));
} }
void JSGenericLowering::LowerJSFindNonDefaultConstructor(Node* node) {
ReplaceWithBuiltinCall(node, Builtin::kFindNonDefaultConstructor);
}
void JSGenericLowering::LowerJSHasInPrototypeChain(Node* node) { void JSGenericLowering::LowerJSHasInPrototypeChain(Node* node) {
ReplaceWithRuntimeCall(node, Runtime::kHasInPrototypeChain); ReplaceWithRuntimeCall(node, Runtime::kHasInPrototypeChain);
} }
......
...@@ -80,6 +80,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) { ...@@ -80,6 +80,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
return ReduceJSAsyncFunctionResolve(node); return ReduceJSAsyncFunctionResolve(node);
case IrOpcode::kJSGetSuperConstructor: case IrOpcode::kJSGetSuperConstructor:
return ReduceJSGetSuperConstructor(node); return ReduceJSGetSuperConstructor(node);
case IrOpcode::kJSFindNonDefaultConstructor:
return ReduceJSFindNonDefaultConstructor(node);
case IrOpcode::kJSInstanceOf: case IrOpcode::kJSInstanceOf:
return ReduceJSInstanceOf(node); return ReduceJSInstanceOf(node);
case IrOpcode::kJSHasInPrototypeChain: case IrOpcode::kJSHasInPrototypeChain:
...@@ -540,6 +542,108 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor( ...@@ -540,6 +542,108 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
return NoChange(); 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) { Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
JSInstanceOfNode n(node); JSInstanceOfNode n(node);
FeedbackParameter const& p = n.Parameters(); FeedbackParameter const& p = n.Parameters();
......
...@@ -75,6 +75,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -75,6 +75,7 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Reduction ReduceJSAsyncFunctionReject(Node* node); Reduction ReduceJSAsyncFunctionReject(Node* node);
Reduction ReduceJSAsyncFunctionResolve(Node* node); Reduction ReduceJSAsyncFunctionResolve(Node* node);
Reduction ReduceJSGetSuperConstructor(Node* node); Reduction ReduceJSGetSuperConstructor(Node* node);
Reduction ReduceJSFindNonDefaultConstructor(Node* node);
Reduction ReduceJSInstanceOf(Node* node); Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSHasInPrototypeChain(Node* node); Reduction ReduceJSHasInPrototypeChain(Node* node);
Reduction ReduceJSOrdinaryHasInstance(Node* node); Reduction ReduceJSOrdinaryHasInstance(Node* node);
......
...@@ -770,6 +770,7 @@ Type JSWasmCallNode::TypeForWasmReturnType(const wasm::ValueType& type) { ...@@ -770,6 +770,7 @@ Type JSWasmCallNode::TypeForWasmReturnType(const wasm::ValueType& type) {
V(RejectPromise, Operator::kNoDeopt | Operator::kNoThrow, 3, 1) \ V(RejectPromise, Operator::kNoDeopt | Operator::kNoThrow, 3, 1) \
V(ResolvePromise, Operator::kNoDeopt | Operator::kNoThrow, 2, 1) \ V(ResolvePromise, Operator::kNoDeopt | Operator::kNoThrow, 2, 1) \
V(GetSuperConstructor, Operator::kNoWrite | Operator::kNoThrow, 1, 1) \ V(GetSuperConstructor, Operator::kNoWrite | Operator::kNoThrow, 1, 1) \
V(FindNonDefaultConstructor, Operator::kNoProperties, 2, 2) \
V(ParseInt, Operator::kNoProperties, 2, 1) \ V(ParseInt, Operator::kNoProperties, 2, 1) \
V(RegExpTest, Operator::kNoProperties, 2, 1) V(RegExpTest, Operator::kNoProperties, 2, 1)
......
...@@ -1040,6 +1040,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -1040,6 +1040,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* GetSuperConstructor(); const Operator* GetSuperConstructor();
const Operator* FindNonDefaultConstructor();
const Operator* CreateGeneratorObject(); const Operator* CreateGeneratorObject();
const Operator* LoadGlobal(const NameRef& name, const Operator* LoadGlobal(const NameRef& name,
...@@ -1757,6 +1759,20 @@ class JSForInNextNode final : public JSNodeWrapperBase { ...@@ -1757,6 +1759,20 @@ class JSForInNextNode final : public JSNodeWrapperBase {
#undef INPUTS #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 #undef DEFINE_INPUT_ACCESSORS
} // namespace compiler } // namespace compiler
......
...@@ -183,7 +183,8 @@ ...@@ -183,7 +183,8 @@
V(JSStoreInArrayLiteral) \ V(JSStoreInArrayLiteral) \
V(JSDeleteProperty) \ V(JSDeleteProperty) \
V(JSHasProperty) \ V(JSHasProperty) \
V(JSGetSuperConstructor) V(JSGetSuperConstructor) \
V(JSFindNonDefaultConstructor)
#define JS_CONTEXT_OP_LIST(V) \ #define JS_CONTEXT_OP_LIST(V) \
V(JSHasContextExtension) \ V(JSHasContextExtension) \
......
...@@ -91,6 +91,7 @@ bool OperatorProperties::NeedsExactContext(const Operator* op) { ...@@ -91,6 +91,7 @@ bool OperatorProperties::NeedsExactContext(const Operator* op) {
case IrOpcode::kJSSetNamedProperty: case IrOpcode::kJSSetNamedProperty:
case IrOpcode::kJSDefineNamedOwnProperty: case IrOpcode::kJSDefineNamedOwnProperty:
case IrOpcode::kJSSetKeyedProperty: case IrOpcode::kJSSetKeyedProperty:
case IrOpcode::kJSFindNonDefaultConstructor:
return true; return true;
case IrOpcode::kJSAsyncFunctionEnter: case IrOpcode::kJSAsyncFunctionEnter:
...@@ -239,6 +240,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) { ...@@ -239,6 +240,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSStackCheck: case IrOpcode::kJSStackCheck:
case IrOpcode::kJSDebugger: case IrOpcode::kJSDebugger:
case IrOpcode::kJSGetSuperConstructor: case IrOpcode::kJSGetSuperConstructor:
case IrOpcode::kJSFindNonDefaultConstructor:
case IrOpcode::kJSBitwiseNot: case IrOpcode::kJSBitwiseNot:
case IrOpcode::kJSDecrement: case IrOpcode::kJSDecrement:
case IrOpcode::kJSIncrement: case IrOpcode::kJSIncrement:
......
...@@ -1470,6 +1470,10 @@ Type Typer::Visitor::TypeJSGetSuperConstructor(Node* node) { ...@@ -1470,6 +1470,10 @@ Type Typer::Visitor::TypeJSGetSuperConstructor(Node* node) {
return Type::NonInternal(); return Type::NonInternal();
} }
Type Typer::Visitor::TypeJSFindNonDefaultConstructor(Node* node) {
return Type::Tuple(Type::Boolean(), Type::Object(), zone());
}
// JS context operators. // JS context operators.
Type Typer::Visitor::TypeJSHasContextExtension(Node* node) { Type Typer::Visitor::TypeJSHasContextExtension(Node* node) {
return Type::Boolean(); return Type::Boolean();
......
...@@ -1121,6 +1121,13 @@ Type Type::Tuple(Type first, Type second, Type third, Zone* zone) { ...@@ -1121,6 +1121,13 @@ Type Type::Tuple(Type first, Type second, Type third, Zone* zone) {
return FromTypeBase(tuple); 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 // static
Type Type::OtherNumberConstant(double value, Zone* zone) { Type Type::OtherNumberConstant(double value, Zone* zone) {
return FromTypeBase(OtherNumberConstantType::New(value, zone)); return FromTypeBase(OtherNumberConstantType::New(value, zone));
......
...@@ -423,6 +423,7 @@ class V8_EXPORT_PRIVATE Type { ...@@ -423,6 +423,7 @@ class V8_EXPORT_PRIVATE Type {
static Type Constant(double value, Zone* zone); static Type Constant(double value, Zone* zone);
static Type Range(double min, double max, 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, Type third, Zone* zone);
static Type Tuple(Type first, Type second, Zone* zone);
static Type Union(Type type1, Type type2, Zone* zone); static Type Union(Type type1, Type type2, Zone* zone);
static Type Intersect(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) { ...@@ -790,7 +790,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::Any()); CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::NonInternal()); CheckTypeIs(node, Type::NonInternal());
break; break;
case IrOpcode::kJSFindNonDefaultConstructor:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any());
break;
case IrOpcode::kJSHasContextExtension: case IrOpcode::kJSHasContextExtension:
CheckTypeIs(node, Type::Boolean()); CheckTypeIs(node, Type::Boolean());
break; break;
......
// 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;
})();
// 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 @@ ...@@ -2,9 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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. // TODO(v8:13091): Enable Maglev.
// This behavior is not spec compliant, see crbug.com/v8/13249. // This behavior is not spec compliant, see crbug.com/v8/13249.
......
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // 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. // TODO(v8:13091): Enable Maglev.
(function OmitDefaultBaseCtor() { (function OmitDefaultBaseCtor() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment