Commit 21701297 authored by bmeurer's avatar bmeurer Committed by Commit Bot

[turbofan] Introduce new JSConstructWithArrayLike operator.

Add a new JSConstructWithArrayLike operator that is backed by the
ConstructWithArrayLike builtin (similar to what was done before
for the JSCallWithArrayLike operator), and use that operator to
optimize Reflect.construct inlining in TurboFan. This is handled
uniformly with JSConstructWithSpread in the JSCallReducer.

Also add missing test coverage for Reflect.construct in optimized
code, especially for some interesting corner cases.

R=petermarshall@chromium.org
BUG=v8:4587,v8:5269

Review-Url: https://codereview.chromium.org/2949813002
Cr-Commit-Position: refs/heads/master@{#46087}
parent 72a597fa
...@@ -2070,39 +2070,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { ...@@ -2070,39 +2070,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
// -- sp[0] : receiver (undefined) // -- sp[0] : receiver (undefined)
// ----------------------------------- // -----------------------------------
// 2. Make sure the target is actually a constructor. // 2. We don't need to check explicitly for constructor target here,
Label target_not_constructor; // since that's the first thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(r1, &target_not_constructor); // builtins will do.
__ ldr(r4, FieldMemOperand(r1, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
__ tst(r4, Operand(1 << Map::kIsConstructor));
__ b(eq, &target_not_constructor);
// 3. Make sure the target is actually a constructor. // 3. We don't need to check explicitly for constructor new.target here,
Label new_target_not_constructor; // since that's the second thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(r3, &new_target_not_constructor); // builtins will do.
__ ldr(r4, FieldMemOperand(r3, HeapObject::kMapOffset));
__ ldrb(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
__ tst(r4, Operand(1 << Map::kIsConstructor));
__ b(eq, &new_target_not_constructor);
// 4a. Construct the target with the given new.target and argumentsList. // 4. Construct the target with the given new.target and argumentsList.
__ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(), __ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
// 4b. The target is not a constructor, throw an appropriate TypeError.
__ bind(&target_not_constructor);
{
__ str(r1, MemOperand(sp, 0));
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
// 4c. The new.target is not a constructor, throw an appropriate TypeError.
__ bind(&new_target_not_constructor);
{
__ str(r3, MemOperand(sp, 0));
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
} }
static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
......
...@@ -2175,39 +2175,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { ...@@ -2175,39 +2175,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
// -- jssp[0] : receiver (undefined) // -- jssp[0] : receiver (undefined)
// ----------------------------------- // -----------------------------------
// 2. Make sure the target is actually a constructor. // 2. We don't need to check explicitly for constructor target here,
Label target_not_constructor; // since that's the first thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(target, &target_not_constructor); // builtins will do.
__ Ldr(x10, FieldMemOperand(target, HeapObject::kMapOffset));
__ Ldrb(x10, FieldMemOperand(x10, Map::kBitFieldOffset));
__ TestAndBranchIfAllClear(x10, 1 << Map::kIsConstructor,
&target_not_constructor);
// 3. Make sure the new.target is actually a constructor.
Label new_target_not_constructor;
__ JumpIfSmi(new_target, &new_target_not_constructor);
__ Ldr(x10, FieldMemOperand(new_target, HeapObject::kMapOffset));
__ Ldrb(x10, FieldMemOperand(x10, Map::kBitFieldOffset));
__ TestAndBranchIfAllClear(x10, 1 << Map::kIsConstructor,
&new_target_not_constructor);
// 4a. Construct the target with the given new.target and argumentsList.
__ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
RelocInfo::CODE_TARGET);
// 4b. The target is not a constructor, throw an appropriate TypeError. // 3. We don't need to check explicitly for constructor new.target here,
__ Bind(&target_not_constructor); // since that's the second thing the Construct/ConstructWithArrayLike
{ // builtins will do.
__ Poke(target, 0);
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
// 4c. The new.target is not a constructor, throw an appropriate TypeError. // 4. Construct the target with the given new.target and argumentsList.
__ Bind(&new_target_not_constructor); __ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
{ RelocInfo::CODE_TARGET);
__ Poke(new_target, 0);
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
} }
static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
......
...@@ -120,6 +120,32 @@ void CallOrConstructBuiltinsAssembler::CallOrConstructWithArrayLike( ...@@ -120,6 +120,32 @@ void CallOrConstructBuiltinsAssembler::CallOrConstructWithArrayLike(
Unreachable(); Unreachable();
} }
BIND(&if_target_callable); BIND(&if_target_callable);
} else {
// Check that {target} is a Constructor.
Label if_target_constructor(this),
if_target_not_constructor(this, Label::kDeferred);
GotoIf(TaggedIsSmi(target), &if_target_not_constructor);
Branch(IsConstructor(target), &if_target_constructor,
&if_target_not_constructor);
BIND(&if_target_not_constructor);
{
CallRuntime(Runtime::kThrowNotConstructor, context, target);
Unreachable();
}
BIND(&if_target_constructor);
// Check that {new_target} is a Constructor.
Label if_new_target_constructor(this),
if_new_target_not_constructor(this, Label::kDeferred);
GotoIf(TaggedIsSmi(new_target), &if_new_target_not_constructor);
Branch(IsConstructor(new_target), &if_new_target_constructor,
&if_new_target_not_constructor);
BIND(&if_new_target_not_constructor);
{
CallRuntime(Runtime::kThrowNotConstructor, context, new_target);
Unreachable();
}
BIND(&if_new_target_constructor);
} }
GotoIf(TaggedIsSmi(arguments_list), &if_runtime); GotoIf(TaggedIsSmi(arguments_list), &if_runtime);
......
...@@ -1858,39 +1858,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { ...@@ -1858,39 +1858,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
// -- esp[4] : receiver (undefined) // -- esp[4] : receiver (undefined)
// ----------------------------------- // -----------------------------------
// 2. Make sure the target is actually a constructor. // 2. We don't need to check explicitly for constructor target here,
Label target_not_constructor; // since that's the first thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(edi, &target_not_constructor, Label::kNear); // builtins will do.
__ mov(ecx, FieldOperand(edi, HeapObject::kMapOffset));
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsConstructor));
__ j(zero, &target_not_constructor, Label::kNear);
// 3. Make sure the target is actually a constructor. // 3. We don't need to check explicitly for constructor new.target here,
Label new_target_not_constructor; // since that's the second thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(edx, &new_target_not_constructor, Label::kNear); // builtins will do.
__ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
__ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsConstructor));
__ j(zero, &new_target_not_constructor, Label::kNear);
// 4a. Construct the target with the given new.target and argumentsList. // 4. Construct the target with the given new.target and argumentsList.
__ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(), __ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
// 4b. The target is not a constructor, throw an appropriate TypeError.
__ bind(&target_not_constructor);
{
__ mov(Operand(esp, kPointerSize), edi);
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
// 4c. The new.target is not a constructor, throw an appropriate TypeError.
__ bind(&new_target_not_constructor);
{
__ mov(Operand(esp, kPointerSize), edx);
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
} }
void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) {
......
...@@ -2085,39 +2085,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { ...@@ -2085,39 +2085,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
// -- sp[0] : receiver (undefined) // -- sp[0] : receiver (undefined)
// ----------------------------------- // -----------------------------------
// 2. Make sure the target is actually a constructor. // 2. We don't need to check explicitly for constructor target here,
Label target_not_constructor; // since that's the first thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(a1, &target_not_constructor); // builtins will do.
__ lw(t0, FieldMemOperand(a1, HeapObject::kMapOffset));
__ lbu(t0, FieldMemOperand(t0, Map::kBitFieldOffset));
__ And(t0, t0, Operand(1 << Map::kIsConstructor));
__ Branch(&target_not_constructor, eq, t0, Operand(zero_reg));
// 3. Make sure the target is actually a constructor.
Label new_target_not_constructor;
__ JumpIfSmi(a3, &new_target_not_constructor);
__ lw(t0, FieldMemOperand(a3, HeapObject::kMapOffset));
__ lbu(t0, FieldMemOperand(t0, Map::kBitFieldOffset));
__ And(t0, t0, Operand(1 << Map::kIsConstructor));
__ Branch(&new_target_not_constructor, eq, t0, Operand(zero_reg));
// 4a. Construct the target with the given new.target and argumentsList.
__ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
RelocInfo::CODE_TARGET);
// 4b. The target is not a constructor, throw an appropriate TypeError. // 3. We don't need to check explicitly for constructor new.target here,
__ bind(&target_not_constructor); // since that's the second thing the Construct/ConstructWithArrayLike
{ // builtins will do.
__ sw(a1, MemOperand(sp));
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
// 4c. The new.target is not a constructor, throw an appropriate TypeError. // 4. Construct the target with the given new.target and argumentsList.
__ bind(&new_target_not_constructor); __ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
{ RelocInfo::CODE_TARGET);
__ sw(a3, MemOperand(sp));
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
} }
static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
......
...@@ -2101,39 +2101,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { ...@@ -2101,39 +2101,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
// -- sp[0] : receiver (undefined) // -- sp[0] : receiver (undefined)
// ----------------------------------- // -----------------------------------
// 2. Make sure the target is actually a constructor. // 2. We don't need to check explicitly for constructor target here,
Label target_not_constructor; // since that's the first thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(target, &target_not_constructor); // builtins will do.
__ Ld(a4, FieldMemOperand(target, HeapObject::kMapOffset));
__ Lbu(a4, FieldMemOperand(a4, Map::kBitFieldOffset));
__ And(a4, a4, Operand(1 << Map::kIsConstructor));
__ Branch(&target_not_constructor, eq, a4, Operand(zero_reg));
// 3. Make sure the target is actually a constructor.
Label new_target_not_constructor;
__ JumpIfSmi(new_target, &new_target_not_constructor);
__ Ld(a4, FieldMemOperand(new_target, HeapObject::kMapOffset));
__ Lbu(a4, FieldMemOperand(a4, Map::kBitFieldOffset));
__ And(a4, a4, Operand(1 << Map::kIsConstructor));
__ Branch(&new_target_not_constructor, eq, a4, Operand(zero_reg));
// 4a. Construct the target with the given new.target and argumentsList.
__ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
RelocInfo::CODE_TARGET);
// 4b. The target is not a constructor, throw an appropriate TypeError. // 3. We don't need to check explicitly for constructor new.target here,
__ bind(&target_not_constructor); // since that's the second thing the Construct/ConstructWithArrayLike
{ // builtins will do.
__ Sd(target, MemOperand(sp));
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
// 4c. The new.target is not a constructor, throw an appropriate TypeError. // 4. Construct the target with the given new.target and argumentsList.
__ bind(&new_target_not_constructor); __ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
{ RelocInfo::CODE_TARGET);
__ Sd(new_target, MemOperand(sp));
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
} }
static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
......
...@@ -1839,41 +1839,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) { ...@@ -1839,41 +1839,17 @@ void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
// -- rsp[8] : receiver (undefined) // -- rsp[8] : receiver (undefined)
// ----------------------------------- // -----------------------------------
// 2. Make sure the target is actually a constructor. // 2. We don't need to check explicitly for constructor target here,
Label target_not_constructor; // since that's the first thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(rdi, &target_not_constructor, Label::kNear); // builtins will do.
__ movp(rcx, FieldOperand(rdi, HeapObject::kMapOffset));
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsConstructor));
__ j(zero, &target_not_constructor, Label::kNear);
// 3. Make sure the target is actually a constructor. // 3. We don't need to check explicitly for constructor new.target here,
Label new_target_not_constructor; // since that's the second thing the Construct/ConstructWithArrayLike
__ JumpIfSmi(rdx, &new_target_not_constructor, Label::kNear); // builtins will do.
__ movp(rcx, FieldOperand(rdx, HeapObject::kMapOffset));
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsConstructor));
__ j(zero, &new_target_not_constructor, Label::kNear);
// 4a. Construct the target with the given new.target and argumentsList. // 4. Construct the target with the given new.target and argumentsList.
__ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(), __ Jump(masm->isolate()->builtins()->ConstructWithArrayLike(),
RelocInfo::CODE_TARGET); RelocInfo::CODE_TARGET);
// 4b. The target is not a constructor, throw an appropriate TypeError.
__ bind(&target_not_constructor);
{
StackArgumentsAccessor args(rsp, 0);
__ movp(args.GetReceiverOperand(), rdi);
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
// 4c. The new.target is not a constructor, throw an appropriate TypeError.
__ bind(&new_target_not_constructor);
{
StackArgumentsAccessor args(rsp, 0);
__ movp(args.GetReceiverOperand(), rdx);
__ TailCallRuntime(Runtime::kThrowNotConstructor);
}
} }
void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) { void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) {
......
...@@ -3204,6 +3204,10 @@ Node* CodeStubAssembler::IsConstructorMap(Node* map) { ...@@ -3204,6 +3204,10 @@ Node* CodeStubAssembler::IsConstructorMap(Node* map) {
return IsSetWord32(LoadMapBitField(map), 1 << Map::kIsConstructor); return IsSetWord32(LoadMapBitField(map), 1 << Map::kIsConstructor);
} }
Node* CodeStubAssembler::IsConstructor(Node* object) {
return IsConstructorMap(LoadMap(object));
}
Node* CodeStubAssembler::IsSpecialReceiverInstanceType(Node* instance_type) { Node* CodeStubAssembler::IsSpecialReceiverInstanceType(Node* instance_type) {
STATIC_ASSERT(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE); STATIC_ASSERT(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE);
return Int32LessThanOrEqual(instance_type, return Int32LessThanOrEqual(instance_type,
......
...@@ -774,6 +774,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -774,6 +774,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* IsCallable(Node* object); Node* IsCallable(Node* object);
Node* IsConsStringInstanceType(Node* instance_type); Node* IsConsStringInstanceType(Node* instance_type);
Node* IsConstructorMap(Node* map); Node* IsConstructorMap(Node* map);
Node* IsConstructor(Node* object);
Node* IsDeprecatedMap(Node* map); Node* IsDeprecatedMap(Node* map);
Node* IsDictionary(Node* object); Node* IsDictionary(Node* object);
Node* IsExternalStringInstanceType(Node* instance_type); Node* IsExternalStringInstanceType(Node* instance_type);
......
...@@ -24,6 +24,8 @@ Reduction JSCallReducer::Reduce(Node* node) { ...@@ -24,6 +24,8 @@ Reduction JSCallReducer::Reduce(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kJSConstruct: case IrOpcode::kJSConstruct:
return ReduceJSConstruct(node); return ReduceJSConstruct(node);
case IrOpcode::kJSConstructWithArrayLike:
return ReduceJSConstructWithArrayLike(node);
case IrOpcode::kJSConstructWithSpread: case IrOpcode::kJSConstructWithSpread:
return ReduceJSConstructWithSpread(node); return ReduceJSConstructWithSpread(node);
case IrOpcode::kJSCall: case IrOpcode::kJSCall:
...@@ -436,6 +438,30 @@ Reduction JSCallReducer::ReduceReflectApply(Node* node) { ...@@ -436,6 +438,30 @@ Reduction JSCallReducer::ReduceReflectApply(Node* node) {
return reduction.Changed() ? reduction : Changed(node); return reduction.Changed() ? reduction : Changed(node);
} }
// ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] )
Reduction JSCallReducer::ReduceReflectConstruct(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
int arity = static_cast<int>(p.arity() - 2);
DCHECK_LE(0, arity);
// Massage value inputs appropriately.
node->RemoveInput(0);
node->RemoveInput(0);
while (arity < 2) {
node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
}
if (arity < 3) {
node->InsertInput(graph()->zone(), arity++, node->InputAt(0));
}
while (arity-- > 3) {
node->RemoveInput(arity);
}
NodeProperties::ChangeOp(node,
javascript()->ConstructWithArrayLike(p.frequency()));
Reduction const reduction = ReduceJSConstructWithArrayLike(node);
return reduction.Changed() ? reduction : Changed(node);
}
// ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) // ES6 section 26.1.7 Reflect.getPrototypeOf ( target )
Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
...@@ -664,6 +690,7 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( ...@@ -664,6 +690,7 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency) { Node* node, int arity, CallFrequency const& frequency) {
DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike || DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike ||
node->opcode() == IrOpcode::kJSCallWithSpread || node->opcode() == IrOpcode::kJSCallWithSpread ||
node->opcode() == IrOpcode::kJSConstructWithArrayLike ||
node->opcode() == IrOpcode::kJSConstructWithSpread); node->opcode() == IrOpcode::kJSConstructWithSpread);
// In case of a call/construct with spread, we need to // In case of a call/construct with spread, we need to
...@@ -855,6 +882,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -855,6 +882,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceObjectPrototypeIsPrototypeOf(node); return ReduceObjectPrototypeIsPrototypeOf(node);
case Builtins::kReflectApply: case Builtins::kReflectApply:
return ReduceReflectApply(node); return ReduceReflectApply(node);
case Builtins::kReflectConstruct:
return ReduceReflectConstruct(node);
case Builtins::kReflectGetPrototypeOf: case Builtins::kReflectGetPrototypeOf:
return ReduceReflectGetPrototypeOf(node); return ReduceReflectGetPrototypeOf(node);
case Builtins::kArrayForEach: case Builtins::kArrayForEach:
...@@ -1118,6 +1147,12 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { ...@@ -1118,6 +1147,12 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
return NoChange(); return NoChange();
} }
Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode());
CallFrequency frequency = CallFrequencyOf(node->op());
return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency);
}
Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode()); DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode());
SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op());
......
...@@ -52,11 +52,13 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -52,11 +52,13 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceObjectPrototypeGetProto(Node* node); Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceObjectPrototypeIsPrototypeOf(Node* node); Reduction ReduceObjectPrototypeIsPrototypeOf(Node* node);
Reduction ReduceReflectApply(Node* node); Reduction ReduceReflectApply(Node* node);
Reduction ReduceReflectConstruct(Node* node);
Reduction ReduceReflectGetPrototypeOf(Node* node); Reduction ReduceReflectGetPrototypeOf(Node* node);
Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node); Reduction ReduceArrayForEach(Handle<JSFunction> function, Node* node);
Reduction ReduceCallOrConstructWithArrayLikeOrSpread( Reduction ReduceCallOrConstructWithArrayLikeOrSpread(
Node* node, int arity, CallFrequency const& frequency); Node* node, int arity, CallFrequency const& frequency);
Reduction ReduceJSConstruct(Node* node); Reduction ReduceJSConstruct(Node* node);
Reduction ReduceJSConstructWithArrayLike(Node* node);
Reduction ReduceJSConstructWithSpread(Node* node); Reduction ReduceJSConstructWithSpread(Node* node);
Reduction ReduceJSCall(Node* node); Reduction ReduceJSCall(Node* node);
Reduction ReduceJSCallWithArrayLike(Node* node); Reduction ReduceJSCallWithArrayLike(Node* node);
......
...@@ -589,6 +589,23 @@ void JSGenericLowering::LowerJSConstruct(Node* node) { ...@@ -589,6 +589,23 @@ void JSGenericLowering::LowerJSConstruct(Node* node) {
NodeProperties::ChangeOp(node, common()->Call(desc)); NodeProperties::ChangeOp(node, common()->Call(desc));
} }
void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
Callable callable =
Builtins::CallableFor(isolate(), Builtins::kConstructWithArrayLike);
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), zone(), callable.descriptor(), 1, flags);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* receiver = jsgraph()->UndefinedConstant();
Node* arguments_list = node->InputAt(1);
Node* new_target = node->InputAt(2);
node->InsertInput(zone(), 0, stub_code);
node->ReplaceInput(2, new_target);
node->ReplaceInput(3, arguments_list);
node->InsertInput(zone(), 4, receiver);
NodeProperties::ChangeOp(node, common()->Call(desc));
}
void JSGenericLowering::LowerJSConstructWithSpread(Node* node) { void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op()); SpreadWithArityParameter const& p = SpreadWithArityParameterOf(node->op());
int const arg_count = static_cast<int>(p.arity() - 2); int const arg_count = static_cast<int>(p.arity() - 2);
......
...@@ -23,7 +23,8 @@ std::ostream& operator<<(std::ostream& os, CallFrequency f) { ...@@ -23,7 +23,8 @@ std::ostream& operator<<(std::ostream& os, CallFrequency f) {
} }
CallFrequency CallFrequencyOf(Operator const* op) { CallFrequency CallFrequencyOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, op->opcode()); DCHECK(op->opcode() == IrOpcode::kJSCallWithArrayLike ||
op->opcode() == IrOpcode::kJSConstructWithArrayLike);
return OpParameter<CallFrequency>(op); return OpParameter<CallFrequency>(op);
} }
...@@ -888,6 +889,16 @@ const Operator* JSOperatorBuilder::Construct(uint32_t arity, ...@@ -888,6 +889,16 @@ const Operator* JSOperatorBuilder::Construct(uint32_t arity,
parameters); // parameter parameters); // parameter
} }
const Operator* JSOperatorBuilder::ConstructWithArrayLike(
CallFrequency frequency) {
return new (zone()) Operator1<CallFrequency>( // --
IrOpcode::kJSConstructWithArrayLike, // opcode
Operator::kNoProperties, // properties
"JSConstructWithArrayLike", // name
3, 1, 1, 1, 1, 2, // counts
frequency); // parameter
}
const Operator* JSOperatorBuilder::ConstructWithSpread(uint32_t arity) { const Operator* JSOperatorBuilder::ConstructWithSpread(uint32_t arity) {
SpreadWithArityParameter parameters(arity); SpreadWithArityParameter parameters(arity);
return new (zone()) Operator1<SpreadWithArityParameter>( // -- return new (zone()) Operator1<SpreadWithArityParameter>( // --
......
...@@ -743,6 +743,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -743,6 +743,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* Construct(uint32_t arity, const Operator* Construct(uint32_t arity,
CallFrequency frequency = CallFrequency(), CallFrequency frequency = CallFrequency(),
VectorSlotPair const& feedback = VectorSlotPair()); VectorSlotPair const& feedback = VectorSlotPair());
const Operator* ConstructWithArrayLike(CallFrequency frequency);
const Operator* ConstructWithSpread(uint32_t arity); const Operator* ConstructWithSpread(uint32_t arity);
const Operator* ConvertReceiver(ConvertReceiverMode convert_mode); const Operator* ConvertReceiver(ConvertReceiverMode convert_mode);
......
...@@ -163,6 +163,7 @@ ...@@ -163,6 +163,7 @@
#define JS_OTHER_OP_LIST(V) \ #define JS_OTHER_OP_LIST(V) \
V(JSConstructForwardVarargs) \ V(JSConstructForwardVarargs) \
V(JSConstruct) \ V(JSConstruct) \
V(JSConstructWithArrayLike) \
V(JSConstructWithSpread) \ V(JSConstructWithSpread) \
V(JSCallForwardVarargs) \ V(JSCallForwardVarargs) \
V(JSCall) \ V(JSCall) \
......
...@@ -100,6 +100,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) { ...@@ -100,6 +100,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
// Call operations // Call operations
case IrOpcode::kJSConstructForwardVarargs: case IrOpcode::kJSConstructForwardVarargs:
case IrOpcode::kJSConstruct: case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithArrayLike:
case IrOpcode::kJSConstructWithSpread: case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSCallForwardVarargs: case IrOpcode::kJSCallForwardVarargs:
case IrOpcode::kJSCall: case IrOpcode::kJSCall:
......
...@@ -1342,6 +1342,10 @@ Type* Typer::Visitor::TypeJSConstructForwardVarargs(Node* node) { ...@@ -1342,6 +1342,10 @@ Type* Typer::Visitor::TypeJSConstructForwardVarargs(Node* node) {
Type* Typer::Visitor::TypeJSConstruct(Node* node) { return Type::Receiver(); } Type* Typer::Visitor::TypeJSConstruct(Node* node) { return Type::Receiver(); }
Type* Typer::Visitor::TypeJSConstructWithArrayLike(Node* node) {
return Type::Receiver();
}
Type* Typer::Visitor::TypeJSConstructWithSpread(Node* node) { Type* Typer::Visitor::TypeJSConstructWithSpread(Node* node) {
return Type::Receiver(); return Type::Receiver();
} }
......
...@@ -714,6 +714,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -714,6 +714,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kJSConstructForwardVarargs: case IrOpcode::kJSConstructForwardVarargs:
case IrOpcode::kJSConstruct: case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithArrayLike:
case IrOpcode::kJSConstructWithSpread: case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSConvertReceiver: case IrOpcode::kJSConvertReceiver:
// Type is Receiver. // Type is Receiver.
......
// Copyright 2017 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: --allow-natives-syntax
// Test Reflect.construct with wrong number of arguments.
(function() {
"use strict";
function A() {}
function foo() { return Reflect.construct(A); }
assertThrows(foo);
assertThrows(foo);
%OptimizeFunctionOnNextCall(foo);
assertThrows(foo);
})();
(function() {
"use strict";
function A(x) { this.x = x; }
function foo() { return Reflect.construct(A, arguments); }
assertInstanceof(foo(), A);
assertInstanceof(foo(), A);
assertEquals(1, foo(1).x);
%OptimizeFunctionOnNextCall(foo);
assertInstanceof(foo(), A);
assertEquals(1, foo(1).x);
})();
(function() {
"use strict";
function A(x) { this.x = x; }
function foo() { return Reflect.construct(A, arguments, A, A); }
assertInstanceof(foo(), A);
assertInstanceof(foo(), A);
assertEquals(1, foo(1).x);
%OptimizeFunctionOnNextCall(foo);
assertInstanceof(foo(), A);
assertEquals(1, foo(1).x);
})();
// Test Reflect.construct within try/catch.
(function() {
"use strict";
function foo(bar) {
try {
return Reflect.construct(bar, arguments, bar);
} catch (e) {
return 1;
}
}
assertEquals(1, foo());
assertEquals(1, foo());
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo());
})();
(function() {
"use strict";
function foo(bar) {
try {
return Reflect.construct(bar, bar, bar);
} catch (e) {
return 1;
}
}
assertEquals(1, foo());
assertEquals(1, foo());
%OptimizeFunctionOnNextCall(foo);
assertEquals(1, foo());
})();
// Test proper order of constructor check(s) and array-like iteration.
(function() {
var dummy_length_counter = 0;
var dummy = { get length() { ++dummy_length_counter; return 0; } };
function foo() {
return Reflect.construct(undefined, dummy, undefined);
}
assertThrows(foo, TypeError);
assertThrows(foo, TypeError);
%OptimizeFunctionOnNextCall(foo);
assertThrows(foo, TypeError);
assertEquals(0, dummy_length_counter);
})();
(function() {
var dummy_length_counter = 0;
var dummy = { get length() { ++dummy_length_counter; return 0; } };
function foo() {
return Reflect.construct(undefined, dummy);
}
assertThrows(foo, TypeError);
assertThrows(foo, TypeError);
%OptimizeFunctionOnNextCall(foo);
assertThrows(foo, TypeError);
assertEquals(0, dummy_length_counter);
})();
(function() {
var dummy_length_counter = 0;
var dummy = { get length() { ++dummy_length_counter; return 0; } };
function foo() {
return Reflect.construct(null, dummy, null);
}
assertThrows(foo, TypeError);
assertThrows(foo, TypeError);
%OptimizeFunctionOnNextCall(foo);
assertThrows(foo, TypeError);
assertEquals(0, dummy_length_counter);
})();(function() {
var dummy_length_counter = 0;
var dummy = { get length() { ++dummy_length_counter; return 0; } };
function foo() {
return Reflect.construct(null, dummy);
}
assertThrows(foo, TypeError);
assertThrows(foo, TypeError);
%OptimizeFunctionOnNextCall(foo);
assertThrows(foo, TypeError);
assertEquals(0, dummy_length_counter);
})();
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