[es6] implement Reflect.apply() & Reflect.construct()
BUG=v8:3900 LOG=N R=dslomov@chromium.org Review URL: https://codereview.chromium.org/913073003 Cr-Commit-Position: refs/heads/master@{#27316}
This commit is contained in:
parent
1ecc161cc8
commit
d21fd15467
1
BUILD.gn
1
BUILD.gn
@ -266,6 +266,7 @@ action("js2c_experimental") {
|
||||
"src/harmony-tostring.js",
|
||||
"src/harmony-templates.js",
|
||||
"src/harmony-regexp.js",
|
||||
"src/harmony-reflect.js"
|
||||
]
|
||||
|
||||
outputs = [
|
||||
|
@ -1336,50 +1336,99 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
const int kIndexOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (2 * kPointerSize);
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (1 * kPointerSize);
|
||||
const int kArgsOffset = 2 * kPointerSize;
|
||||
const int kRecvOffset = 3 * kPointerSize;
|
||||
const int kFunctionOffset = 4 * kPointerSize;
|
||||
static void Generate_CheckStackOverflow(MacroAssembler* masm,
|
||||
const int calleeOffset) {
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
Label okay;
|
||||
__ LoadRoot(r2, Heap::kRealStackLimitRootIndex);
|
||||
// Make r2 the space we have left. The stack might already be overflowed
|
||||
// here which will cause r2 to become negative.
|
||||
__ sub(r2, sp, r2);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmp(r2, Operand::PointerOffsetFromSmiKey(r0));
|
||||
__ b(gt, &okay); // Signed comparison.
|
||||
|
||||
// Out of stack space.
|
||||
__ ldr(r1, MemOperand(fp, calleeOffset));
|
||||
__ Push(r1, r0);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
|
||||
__ bind(&okay);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_PushAppliedArguments(MacroAssembler* masm,
|
||||
const int argumentsOffset,
|
||||
const int indexOffset,
|
||||
const int limitOffset) {
|
||||
Label entry, loop;
|
||||
__ ldr(r0, MemOperand(fp, indexOffset));
|
||||
__ b(&entry);
|
||||
|
||||
// Load the current argument from the arguments array and push it to the
|
||||
// stack.
|
||||
// r0: current argument index
|
||||
__ bind(&loop);
|
||||
__ ldr(r1, MemOperand(fp, argumentsOffset));
|
||||
__ Push(r1, r0);
|
||||
|
||||
// Call the runtime to access the property in the arguments array.
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
__ push(r0);
|
||||
|
||||
// Use inline caching to access the arguments.
|
||||
__ ldr(r0, MemOperand(fp, indexOffset));
|
||||
__ add(r0, r0, Operand(1 << kSmiTagSize));
|
||||
__ str(r0, MemOperand(fp, indexOffset));
|
||||
|
||||
// Test if the copy loop has finished copying all the elements from the
|
||||
// arguments object.
|
||||
__ bind(&entry);
|
||||
__ ldr(r1, MemOperand(fp, limitOffset));
|
||||
__ cmp(r0, r1);
|
||||
__ b(ne, &loop);
|
||||
|
||||
// On exit, the pushed arguments count is in r0, untagged
|
||||
__ SmiUntag(r0);
|
||||
}
|
||||
|
||||
|
||||
// Used by FunctionApply and ReflectApply
|
||||
static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) {
|
||||
const int kFormalParameters = targetIsArgument ? 3 : 2;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
{
|
||||
FrameAndConstantPoolScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
const int kArgumentsOffset = kFPOnStackSize + kPCOnStackSize;
|
||||
const int kReceiverOffset = kArgumentsOffset + kPointerSize;
|
||||
const int kFunctionOffset = kReceiverOffset + kPointerSize;
|
||||
|
||||
__ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function
|
||||
__ push(r0);
|
||||
__ ldr(r0, MemOperand(fp, kArgsOffset)); // get the args array
|
||||
__ ldr(r0, MemOperand(fp, kArgumentsOffset)); // get the args array
|
||||
__ push(r0);
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
if (targetIsArgument) {
|
||||
__ InvokeBuiltin(Builtins::REFLECT_APPLY_PREPARE, CALL_FUNCTION);
|
||||
} else {
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
}
|
||||
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
Label okay;
|
||||
__ LoadRoot(r2, Heap::kRealStackLimitRootIndex);
|
||||
// Make r2 the space we have left. The stack might already be overflowed
|
||||
// here which will cause r2 to become negative.
|
||||
__ sub(r2, sp, r2);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmp(r2, Operand::PointerOffsetFromSmiKey(r0));
|
||||
__ b(gt, &okay); // Signed comparison.
|
||||
|
||||
// Out of stack space.
|
||||
__ ldr(r1, MemOperand(fp, kFunctionOffset));
|
||||
__ Push(r1, r0);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
// End of stack check.
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
// Push current limit and index.
|
||||
__ bind(&okay);
|
||||
const int kIndexOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (2 * kPointerSize);
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (1 * kPointerSize);
|
||||
__ push(r0); // limit
|
||||
__ mov(r1, Operand::Zero()); // initial index
|
||||
__ push(r1);
|
||||
|
||||
// Get the receiver.
|
||||
__ ldr(r0, MemOperand(fp, kRecvOffset));
|
||||
__ ldr(r0, MemOperand(fp, kReceiverOffset));
|
||||
|
||||
// Check that the function is a JS function (otherwise it must be a proxy).
|
||||
Label push_receiver;
|
||||
@ -1436,44 +1485,19 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
__ push(r0);
|
||||
|
||||
// Copy all arguments from the array to the stack.
|
||||
Label entry, loop;
|
||||
__ ldr(r0, MemOperand(fp, kIndexOffset));
|
||||
__ b(&entry);
|
||||
|
||||
// Load the current argument from the arguments array and push it to the
|
||||
// stack.
|
||||
// r0: current argument index
|
||||
__ bind(&loop);
|
||||
__ ldr(r1, MemOperand(fp, kArgsOffset));
|
||||
__ Push(r1, r0);
|
||||
|
||||
// Call the runtime to access the property in the arguments array.
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
__ push(r0);
|
||||
|
||||
// Use inline caching to access the arguments.
|
||||
__ ldr(r0, MemOperand(fp, kIndexOffset));
|
||||
__ add(r0, r0, Operand(1 << kSmiTagSize));
|
||||
__ str(r0, MemOperand(fp, kIndexOffset));
|
||||
|
||||
// Test if the copy loop has finished copying all the elements from the
|
||||
// arguments object.
|
||||
__ bind(&entry);
|
||||
__ ldr(r1, MemOperand(fp, kLimitOffset));
|
||||
__ cmp(r0, r1);
|
||||
__ b(ne, &loop);
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
// Call the function.
|
||||
Label call_proxy;
|
||||
ParameterCount actual(r0);
|
||||
__ SmiUntag(r0);
|
||||
__ ldr(r1, MemOperand(fp, kFunctionOffset));
|
||||
__ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
|
||||
__ b(ne, &call_proxy);
|
||||
__ InvokeFunction(r1, actual, CALL_FUNCTION, NullCallWrapper());
|
||||
|
||||
frame_scope.GenerateLeaveFrame();
|
||||
__ add(sp, sp, Operand(3 * kPointerSize));
|
||||
__ add(sp, sp, Operand(kStackSize * kPointerSize));
|
||||
__ Jump(lr);
|
||||
|
||||
// Call the function proxy.
|
||||
@ -1487,11 +1511,91 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
|
||||
// Tear down the internal frame and remove function, receiver and args.
|
||||
}
|
||||
__ add(sp, sp, Operand(3 * kPointerSize));
|
||||
__ add(sp, sp, Operand(kStackSize * kPointerSize));
|
||||
__ Jump(lr);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_ConstructHelper(MacroAssembler* masm) {
|
||||
const int kFormalParameters = 3;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
{
|
||||
FrameAndConstantPoolScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
const int kNewTargetOffset = kFPOnStackSize + kPCOnStackSize;
|
||||
const int kArgumentsOffset = kNewTargetOffset + kPointerSize;
|
||||
const int kFunctionOffset = kArgumentsOffset + kPointerSize;
|
||||
|
||||
// If newTarget is not supplied, set it to constructor
|
||||
Label validate_arguments;
|
||||
__ ldr(r0, MemOperand(fp, kNewTargetOffset));
|
||||
__ CompareRoot(r0, Heap::kUndefinedValueRootIndex);
|
||||
__ b(ne, &validate_arguments);
|
||||
__ ldr(r0, MemOperand(fp, kFunctionOffset));
|
||||
__ str(r0, MemOperand(fp, kNewTargetOffset));
|
||||
|
||||
// Validate arguments
|
||||
__ bind(&validate_arguments);
|
||||
__ ldr(r0, MemOperand(fp, kFunctionOffset)); // get the function
|
||||
__ push(r0);
|
||||
__ ldr(r0, MemOperand(fp, kArgumentsOffset)); // get the args array
|
||||
__ push(r0);
|
||||
__ ldr(r0, MemOperand(fp, kNewTargetOffset)); // get the new.target
|
||||
__ push(r0);
|
||||
__ InvokeBuiltin(Builtins::REFLECT_CONSTRUCT_PREPARE, CALL_FUNCTION);
|
||||
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
// Push current limit and index.
|
||||
const int kIndexOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (2 * kPointerSize);
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (1 * kPointerSize);
|
||||
__ push(r0); // limit
|
||||
__ mov(r1, Operand::Zero()); // initial index
|
||||
__ push(r1);
|
||||
// Push newTarget and callee functions
|
||||
__ ldr(r0, MemOperand(fp, kNewTargetOffset));
|
||||
__ push(r0);
|
||||
__ ldr(r0, MemOperand(fp, kFunctionOffset));
|
||||
__ push(r0);
|
||||
|
||||
// Copy all arguments from the array to the stack.
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
// Use undefined feedback vector
|
||||
__ LoadRoot(r2, Heap::kUndefinedValueRootIndex);
|
||||
__ ldr(r1, MemOperand(fp, kFunctionOffset));
|
||||
|
||||
// Call the function.
|
||||
CallConstructStub stub(masm->isolate(), SUPER_CONSTRUCTOR_CALL);
|
||||
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
__ Drop(1);
|
||||
|
||||
// Leave internal frame.
|
||||
}
|
||||
__ add(sp, sp, Operand(kStackSize * kPointerSize));
|
||||
__ Jump(lr);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
Generate_ApplyHelper(masm, false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectApply(MacroAssembler* masm) {
|
||||
Generate_ApplyHelper(masm, true);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
|
||||
Generate_ConstructHelper(masm);
|
||||
}
|
||||
|
||||
|
||||
static void ArgumentAdaptorStackCheck(MacroAssembler* masm,
|
||||
Label* stack_overflow) {
|
||||
// ----------- S t a t e -------------
|
||||
|
@ -1324,53 +1324,107 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
ASM_LOCATION("Builtins::Generate_FunctionApply");
|
||||
const int kIndexOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (2 * kPointerSize);
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (1 * kPointerSize);
|
||||
const int kArgsOffset = 2 * kPointerSize;
|
||||
const int kReceiverOffset = 3 * kPointerSize;
|
||||
const int kFunctionOffset = 4 * kPointerSize;
|
||||
static void Generate_CheckStackOverflow(MacroAssembler* masm,
|
||||
const int calleeOffset) {
|
||||
Register argc = x0;
|
||||
Register receiver = x14;
|
||||
Register function = x15;
|
||||
|
||||
// Check the stack for overflow.
|
||||
// We are not trying to catch interruptions (e.g. debug break and
|
||||
// preemption) here, so the "real stack limit" is checked.
|
||||
Label enough_stack_space;
|
||||
__ LoadRoot(x10, Heap::kRealStackLimitRootIndex);
|
||||
__ Ldr(function, MemOperand(fp, calleeOffset));
|
||||
// Make x10 the space we have left. The stack might already be overflowed
|
||||
// here which will cause x10 to become negative.
|
||||
// TODO(jbramley): Check that the stack usage here is safe.
|
||||
__ Sub(x10, jssp, x10);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ Cmp(x10, Operand::UntagSmiAndScale(argc, kPointerSizeLog2));
|
||||
__ B(gt, &enough_stack_space);
|
||||
// There is not enough stack space, so use a builtin to throw an appropriate
|
||||
// error.
|
||||
__ Push(function, argc);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
// We should never return from the APPLY_OVERFLOW builtin.
|
||||
if (__ emit_debug_code()) {
|
||||
__ Unreachable();
|
||||
}
|
||||
|
||||
__ Bind(&enough_stack_space);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_PushAppliedArguments(MacroAssembler* masm,
|
||||
const int argumentsOffset,
|
||||
const int indexOffset,
|
||||
const int limitOffset) {
|
||||
Label entry, loop;
|
||||
Register current = x0;
|
||||
__ Ldr(current, MemOperand(fp, indexOffset));
|
||||
__ B(&entry);
|
||||
|
||||
__ Bind(&loop);
|
||||
// Load the current argument from the arguments array and push it.
|
||||
// TODO(all): Couldn't we optimize this for JS arrays?
|
||||
|
||||
__ Ldr(x1, MemOperand(fp, argumentsOffset));
|
||||
__ Push(x1, current);
|
||||
|
||||
// Call the runtime to access the property in the arguments array.
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
__ Push(x0);
|
||||
|
||||
// Use inline caching to access the arguments.
|
||||
__ Ldr(current, MemOperand(fp, indexOffset));
|
||||
__ Add(current, current, Smi::FromInt(1));
|
||||
__ Str(current, MemOperand(fp, indexOffset));
|
||||
|
||||
// Test if the copy loop has finished copying all the elements from the
|
||||
// arguments object.
|
||||
__ Bind(&entry);
|
||||
__ Ldr(x1, MemOperand(fp, limitOffset));
|
||||
__ Cmp(current, x1);
|
||||
__ B(ne, &loop);
|
||||
|
||||
// On exit, the pushed arguments count is in x0, untagged
|
||||
__ SmiUntag(current);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) {
|
||||
const int kFormalParameters = targetIsArgument ? 3 : 2;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
const int kArgumentsOffset = kFPOnStackSize + kPCOnStackSize;
|
||||
const int kReceiverOffset = kArgumentsOffset + kPointerSize;
|
||||
const int kFunctionOffset = kReceiverOffset + kPointerSize;
|
||||
const int kIndexOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (2 * kPointerSize);
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (1 * kPointerSize);
|
||||
|
||||
Register args = x12;
|
||||
Register receiver = x14;
|
||||
Register function = x15;
|
||||
|
||||
// Get the length of the arguments via a builtin call.
|
||||
__ Ldr(function, MemOperand(fp, kFunctionOffset));
|
||||
__ Ldr(args, MemOperand(fp, kArgsOffset));
|
||||
__ Ldr(args, MemOperand(fp, kArgumentsOffset));
|
||||
__ Push(function, args);
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
if (targetIsArgument) {
|
||||
__ InvokeBuiltin(Builtins::REFLECT_APPLY_PREPARE, CALL_FUNCTION);
|
||||
} else {
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
}
|
||||
Register argc = x0;
|
||||
|
||||
// Check the stack for overflow.
|
||||
// We are not trying to catch interruptions (e.g. debug break and
|
||||
// preemption) here, so the "real stack limit" is checked.
|
||||
Label enough_stack_space;
|
||||
__ LoadRoot(x10, Heap::kRealStackLimitRootIndex);
|
||||
__ Ldr(function, MemOperand(fp, kFunctionOffset));
|
||||
// Make x10 the space we have left. The stack might already be overflowed
|
||||
// here which will cause x10 to become negative.
|
||||
// TODO(jbramley): Check that the stack usage here is safe.
|
||||
__ Sub(x10, jssp, x10);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ Cmp(x10, Operand::UntagSmiAndScale(argc, kPointerSizeLog2));
|
||||
__ B(gt, &enough_stack_space);
|
||||
// There is not enough stack space, so use a builtin to throw an appropriate
|
||||
// error.
|
||||
__ Push(function, argc);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
// We should never return from the APPLY_OVERFLOW builtin.
|
||||
if (__ emit_debug_code()) {
|
||||
__ Unreachable();
|
||||
}
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
__ Bind(&enough_stack_space);
|
||||
// Push current limit and index.
|
||||
__ Mov(x1, 0); // Initial index.
|
||||
__ Push(argc, x1);
|
||||
@ -1424,33 +1478,8 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
__ Push(receiver);
|
||||
|
||||
// Copy all arguments from the array to the stack.
|
||||
Label entry, loop;
|
||||
Register current = x0;
|
||||
__ Ldr(current, MemOperand(fp, kIndexOffset));
|
||||
__ B(&entry);
|
||||
|
||||
__ Bind(&loop);
|
||||
// Load the current argument from the arguments array and push it.
|
||||
// TODO(all): Couldn't we optimize this for JS arrays?
|
||||
|
||||
__ Ldr(x1, MemOperand(fp, kArgsOffset));
|
||||
__ Push(x1, current);
|
||||
|
||||
// Call the runtime to access the property in the arguments array.
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
__ Push(x0);
|
||||
|
||||
// Use inline caching to access the arguments.
|
||||
__ Ldr(current, MemOperand(fp, kIndexOffset));
|
||||
__ Add(current, current, Smi::FromInt(1));
|
||||
__ Str(current, MemOperand(fp, kIndexOffset));
|
||||
|
||||
// Test if the copy loop has finished copying all the elements from the
|
||||
// arguments object.
|
||||
__ Bind(&entry);
|
||||
__ Ldr(x1, MemOperand(fp, kLimitOffset));
|
||||
__ Cmp(current, x1);
|
||||
__ B(ne, &loop);
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
// At the end of the loop, the number of arguments is stored in 'current',
|
||||
// represented as a smi.
|
||||
@ -1460,12 +1489,11 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
|
||||
// Call the function.
|
||||
Label call_proxy;
|
||||
ParameterCount actual(current);
|
||||
__ SmiUntag(current);
|
||||
ParameterCount actual(x0);
|
||||
__ JumpIfNotObjectType(function, x10, x11, JS_FUNCTION_TYPE, &call_proxy);
|
||||
__ InvokeFunction(function, actual, CALL_FUNCTION, NullCallWrapper());
|
||||
frame_scope.GenerateLeaveFrame();
|
||||
__ Drop(3);
|
||||
__ Drop(kStackSize);
|
||||
__ Ret();
|
||||
|
||||
// Call the function proxy.
|
||||
@ -1479,11 +1507,94 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
__ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
|
||||
RelocInfo::CODE_TARGET);
|
||||
}
|
||||
__ Drop(3);
|
||||
__ Drop(kStackSize);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
static void Generate_ConstructHelper(MacroAssembler* masm) {
|
||||
const int kFormalParameters = 3;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
|
||||
const int kNewTargetOffset = kFPOnStackSize + kPCOnStackSize;
|
||||
const int kArgumentsOffset = kNewTargetOffset + kPointerSize;
|
||||
const int kFunctionOffset = kArgumentsOffset + kPointerSize;
|
||||
|
||||
const int kIndexOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (2 * kPointerSize);
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - (1 * kPointerSize);
|
||||
|
||||
// Is x11 safe to use?
|
||||
Register newTarget = x11;
|
||||
Register args = x12;
|
||||
Register receiver = x14;
|
||||
Register function = x15;
|
||||
|
||||
// If newTarget is not supplied, set it to constructor
|
||||
Label validate_arguments;
|
||||
__ Ldr(x0, MemOperand(fp, kNewTargetOffset));
|
||||
__ CompareRoot(x0, Heap::kUndefinedValueRootIndex);
|
||||
__ B(ne, &validate_arguments);
|
||||
__ Ldr(x0, MemOperand(fp, kFunctionOffset));
|
||||
__ Str(x0, MemOperand(fp, kNewTargetOffset));
|
||||
|
||||
// Validate arguments
|
||||
__ Bind(&validate_arguments);
|
||||
__ Ldr(function, MemOperand(fp, kFunctionOffset));
|
||||
__ Ldr(args, MemOperand(fp, kArgumentsOffset));
|
||||
__ Ldr(newTarget, MemOperand(fp, kNewTargetOffset));
|
||||
__ Push(function, args, newTarget);
|
||||
__ InvokeBuiltin(Builtins::REFLECT_CONSTRUCT_PREPARE, CALL_FUNCTION);
|
||||
Register argc = x0;
|
||||
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
// Push current limit and index, constructor & newTarget
|
||||
__ Mov(x1, 0); // Initial index.
|
||||
__ Ldr(newTarget, MemOperand(fp, kNewTargetOffset));
|
||||
__ Push(argc, x1, newTarget, function);
|
||||
|
||||
// Copy all arguments from the array to the stack.
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
__ Ldr(x1, MemOperand(fp, kFunctionOffset));
|
||||
// Use undefined feedback vector
|
||||
__ LoadRoot(x2, Heap::kUndefinedValueRootIndex);
|
||||
|
||||
// Call the function.
|
||||
CallConstructStub stub(masm->isolate(), SUPER_CONSTRUCTOR_CALL);
|
||||
__ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
__ Drop(1);
|
||||
}
|
||||
__ Drop(kStackSize);
|
||||
__ Ret();
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
ASM_LOCATION("Builtins::Generate_FunctionApply");
|
||||
Generate_ApplyHelper(masm, false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectApply(MacroAssembler* masm) {
|
||||
ASM_LOCATION("Builtins::Generate_ReflectApply");
|
||||
Generate_ApplyHelper(masm, true);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
|
||||
ASM_LOCATION("Builtins::Generate_ReflectConstruct");
|
||||
Generate_ConstructHelper(masm);
|
||||
}
|
||||
|
||||
|
||||
static void ArgumentAdaptorStackCheck(MacroAssembler* masm,
|
||||
Label* stack_overflow) {
|
||||
// ----------- S t a t e -------------
|
||||
|
@ -1667,6 +1667,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_unicode)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_unicode_regexps)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_computed_property_names)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_rest_parameters)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_reflect)
|
||||
|
||||
|
||||
void Genesis::InstallNativeFunctions_harmony_proxies() {
|
||||
@ -1721,6 +1722,48 @@ void Genesis::InitializeGlobal_harmony_unicode_regexps() {
|
||||
}
|
||||
|
||||
|
||||
void Genesis::InitializeGlobal_harmony_reflect() {
|
||||
if (!FLAG_harmony_reflect) return;
|
||||
Handle<JSObject> builtins(native_context()->builtins());
|
||||
// Install references to functions of the Reflect object
|
||||
{
|
||||
Handle<JSFunction> apply =
|
||||
InstallFunction(builtins, "ReflectApply", JS_OBJECT_TYPE,
|
||||
JSObject::kHeaderSize, MaybeHandle<JSObject>(),
|
||||
Builtins::kReflectApply);
|
||||
Handle<JSFunction> construct =
|
||||
InstallFunction(builtins, "ReflectConstruct", JS_OBJECT_TYPE,
|
||||
JSObject::kHeaderSize, MaybeHandle<JSObject>(),
|
||||
Builtins::kReflectConstruct);
|
||||
if (FLAG_vector_ics) {
|
||||
// Apply embeds an IC, so we need a type vector of size 1 in the shared
|
||||
// function info.
|
||||
FeedbackVectorSpec spec(0, Code::CALL_IC);
|
||||
Handle<TypeFeedbackVector> feedback_vector =
|
||||
factory()->NewTypeFeedbackVector(&spec);
|
||||
apply->shared()->set_feedback_vector(*feedback_vector);
|
||||
|
||||
feedback_vector = factory()->NewTypeFeedbackVector(&spec);
|
||||
construct->shared()->set_feedback_vector(*feedback_vector);
|
||||
}
|
||||
|
||||
apply->shared()->set_internal_formal_parameter_count(3);
|
||||
apply->shared()->set_length(3);
|
||||
|
||||
construct->shared()->set_internal_formal_parameter_count(3);
|
||||
construct->shared()->set_length(2);
|
||||
}
|
||||
|
||||
Handle<JSGlobalObject> global(JSGlobalObject::cast(
|
||||
native_context()->global_object()));
|
||||
Handle<String> reflect_string =
|
||||
factory()->NewStringFromStaticChars("Reflect");
|
||||
Handle<Object> reflect =
|
||||
factory()->NewJSObject(isolate()->object_function(), TENURED);
|
||||
JSObject::AddProperty(global, reflect_string, reflect, DONT_ENUM);
|
||||
}
|
||||
|
||||
|
||||
Handle<JSFunction> Genesis::InstallInternalArray(
|
||||
Handle<JSBuiltinsObject> builtins,
|
||||
const char* name,
|
||||
@ -2283,6 +2326,8 @@ bool Genesis::InstallExperimentalNatives() {
|
||||
static const char* harmony_unicode_regexps_natives[] = {NULL};
|
||||
static const char* harmony_computed_property_names_natives[] = {NULL};
|
||||
static const char* harmony_rest_parameters_natives[] = {NULL};
|
||||
static const char* harmony_reflect_natives[] = {"native harmony-reflect.js",
|
||||
NULL};
|
||||
|
||||
for (int i = ExperimentalNatives::GetDebuggerCount();
|
||||
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
|
||||
|
@ -109,6 +109,8 @@ enum BuiltinExtraArguments {
|
||||
/* Uses KeyedLoadIC_Initialize; must be after in list. */ \
|
||||
V(FunctionCall, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(FunctionApply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ReflectApply, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ReflectConstruct, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
\
|
||||
V(InternalArrayCode, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
V(ArrayCode, BUILTIN, UNINITIALIZED, kNoExtraICState) \
|
||||
@ -193,6 +195,8 @@ enum BuiltinExtraArguments {
|
||||
V(STRING_ADD_LEFT, 1) \
|
||||
V(STRING_ADD_RIGHT, 1) \
|
||||
V(APPLY_PREPARE, 1) \
|
||||
V(REFLECT_APPLY_PREPARE, 1) \
|
||||
V(REFLECT_CONSTRUCT_PREPARE, 2) \
|
||||
V(STACK_OVERFLOW, 1)
|
||||
|
||||
class BuiltinFunctionTable;
|
||||
@ -316,6 +320,8 @@ class Builtins {
|
||||
|
||||
static void Generate_FunctionCall(MacroAssembler* masm);
|
||||
static void Generate_FunctionApply(MacroAssembler* masm);
|
||||
static void Generate_ReflectApply(MacroAssembler* masm);
|
||||
static void Generate_ReflectConstruct(MacroAssembler* masm);
|
||||
|
||||
static void Generate_InternalArrayCode(MacroAssembler* masm);
|
||||
static void Generate_ArrayCode(MacroAssembler* masm);
|
||||
|
@ -192,7 +192,8 @@ DEFINE_IMPLICATION(es_staging, harmony)
|
||||
V(harmony_sloppy, "harmony features in sloppy mode") \
|
||||
V(harmony_unicode, "harmony unicode escapes") \
|
||||
V(harmony_unicode_regexps, "harmony unicode regexps") \
|
||||
V(harmony_rest_parameters, "harmony rest parameters")
|
||||
V(harmony_rest_parameters, "harmony rest parameters") \
|
||||
V(harmony_reflect, "harmony Reflect API")
|
||||
|
||||
// Features that are complete (but still behind --harmony/es-staging flag).
|
||||
#define HARMONY_STAGED(V) \
|
||||
|
18
src/harmony-reflect.js
Normal file
18
src/harmony-reflect.js
Normal file
@ -0,0 +1,18 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
'use strict';
|
||||
|
||||
var $Reflect = global.Reflect;
|
||||
|
||||
function SetUpReflect() {
|
||||
%CheckIsBootstrapping();
|
||||
|
||||
InstallFunctions($Reflect, DONT_ENUM, $Array(
|
||||
"apply", ReflectApply,
|
||||
"construct", ReflectConstruct
|
||||
));
|
||||
}
|
||||
|
||||
SetUpReflect();
|
@ -990,42 +990,116 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
static const int kArgumentsOffset = 2 * kPointerSize;
|
||||
static const int kReceiverOffset = 3 * kPointerSize;
|
||||
static const int kFunctionOffset = 4 * kPointerSize;
|
||||
static void Generate_CheckStackOverflow(MacroAssembler* masm,
|
||||
const int calleeOffset) {
|
||||
// eax : the number of items to be pushed to the stack
|
||||
//
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
Label okay;
|
||||
ExternalReference real_stack_limit =
|
||||
ExternalReference::address_of_real_stack_limit(masm->isolate());
|
||||
__ mov(edi, Operand::StaticVariable(real_stack_limit));
|
||||
// Make ecx the space we have left. The stack might already be overflowed
|
||||
// here which will cause ecx to become negative.
|
||||
__ mov(ecx, esp);
|
||||
__ sub(ecx, edi);
|
||||
// Make edx the space we need for the array when it is unrolled onto the
|
||||
// stack.
|
||||
__ mov(edx, eax);
|
||||
__ shl(edx, kPointerSizeLog2 - kSmiTagSize);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmp(ecx, edx);
|
||||
__ j(greater, &okay); // Signed comparison.
|
||||
|
||||
// Out of stack space.
|
||||
__ push(Operand(ebp, calleeOffset)); // push this
|
||||
__ push(eax);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
|
||||
__ bind(&okay);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_PushAppliedArguments(MacroAssembler* masm,
|
||||
const int argumentsOffset,
|
||||
const int indexOffset,
|
||||
const int limitOffset) {
|
||||
// Copy all arguments from the array to the stack.
|
||||
Label entry, loop;
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
__ mov(key, Operand(ebp, indexOffset));
|
||||
__ jmp(&entry);
|
||||
__ bind(&loop);
|
||||
__ mov(receiver, Operand(ebp, argumentsOffset)); // load arguments
|
||||
|
||||
if (FLAG_vector_ics) {
|
||||
// TODO(mvstanton): Vector-based ics need additional infrastructure to
|
||||
// be embedded here. For now, just call the runtime.
|
||||
__ push(receiver);
|
||||
__ push(key);
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
} else {
|
||||
// Use inline caching to speed up access to arguments.
|
||||
Handle<Code> ic = CodeFactory::KeyedLoadIC(masm->isolate()).code();
|
||||
__ call(ic, RelocInfo::CODE_TARGET);
|
||||
// It is important that we do not have a test instruction after the
|
||||
// call. A test instruction after the call is used to indicate that
|
||||
// we have generated an inline version of the keyed load. In this
|
||||
// case, we know that we are not generating a test instruction next.
|
||||
}
|
||||
|
||||
// Push the nth argument.
|
||||
__ push(eax);
|
||||
|
||||
// Update the index on the stack and in register key.
|
||||
__ mov(key, Operand(ebp, indexOffset));
|
||||
__ add(key, Immediate(1 << kSmiTagSize));
|
||||
__ mov(Operand(ebp, indexOffset), key);
|
||||
|
||||
__ bind(&entry);
|
||||
__ cmp(key, Operand(ebp, limitOffset));
|
||||
__ j(not_equal, &loop);
|
||||
|
||||
// On exit, the pushed arguments count is in eax, untagged
|
||||
__ Move(eax, key);
|
||||
__ SmiUntag(eax);
|
||||
}
|
||||
|
||||
|
||||
// Used by FunctionApply and ReflectApply
|
||||
static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) {
|
||||
const int kFormalParameters = targetIsArgument ? 3 : 2;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
// Stack at entry:
|
||||
// esp : return address
|
||||
// esp[4] : arguments
|
||||
// esp[8] : receiver ("this")
|
||||
// esp[12] : function
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Stack frame:
|
||||
// ebp : Old base pointer
|
||||
// ebp[4] : return address
|
||||
// ebp[8] : function arguments
|
||||
// ebp[12] : receiver
|
||||
// ebp[16] : function
|
||||
static const int kArgumentsOffset = kFPOnStackSize + kPCOnStackSize;
|
||||
static const int kReceiverOffset = kArgumentsOffset + kPointerSize;
|
||||
static const int kFunctionOffset = kReceiverOffset + kPointerSize;
|
||||
|
||||
__ push(Operand(ebp, kFunctionOffset)); // push this
|
||||
__ push(Operand(ebp, kArgumentsOffset)); // push arguments
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
if (targetIsArgument) {
|
||||
__ InvokeBuiltin(Builtins::REFLECT_APPLY_PREPARE, CALL_FUNCTION);
|
||||
} else {
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
}
|
||||
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
Label okay;
|
||||
ExternalReference real_stack_limit =
|
||||
ExternalReference::address_of_real_stack_limit(masm->isolate());
|
||||
__ mov(edi, Operand::StaticVariable(real_stack_limit));
|
||||
// Make ecx the space we have left. The stack might already be overflowed
|
||||
// here which will cause ecx to become negative.
|
||||
__ mov(ecx, esp);
|
||||
__ sub(ecx, edi);
|
||||
// Make edx the space we need for the array when it is unrolled onto the
|
||||
// stack.
|
||||
__ mov(edx, eax);
|
||||
__ shl(edx, kPointerSizeLog2 - kSmiTagSize);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmp(ecx, edx);
|
||||
__ j(greater, &okay); // Signed comparison.
|
||||
|
||||
// Out of stack space.
|
||||
__ push(Operand(ebp, 4 * kPointerSize)); // push this
|
||||
__ push(eax);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
__ bind(&okay);
|
||||
// End of stack check.
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
// Push current index and limit.
|
||||
const int kLimitOffset =
|
||||
@ -1088,55 +1162,20 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
__ bind(&push_receiver);
|
||||
__ push(ebx);
|
||||
|
||||
// Copy all arguments from the array to the stack.
|
||||
Label entry, loop;
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
__ mov(key, Operand(ebp, kIndexOffset));
|
||||
__ jmp(&entry);
|
||||
__ bind(&loop);
|
||||
__ mov(receiver, Operand(ebp, kArgumentsOffset)); // load arguments
|
||||
|
||||
if (FLAG_vector_ics) {
|
||||
// TODO(mvstanton): Vector-based ics need additional infrastructure to
|
||||
// be embedded here. For now, just call the runtime.
|
||||
__ push(receiver);
|
||||
__ push(key);
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
} else {
|
||||
// Use inline caching to speed up access to arguments.
|
||||
Handle<Code> ic = CodeFactory::KeyedLoadIC(masm->isolate()).code();
|
||||
__ call(ic, RelocInfo::CODE_TARGET);
|
||||
// It is important that we do not have a test instruction after the
|
||||
// call. A test instruction after the call is used to indicate that
|
||||
// we have generated an inline version of the keyed load. In this
|
||||
// case, we know that we are not generating a test instruction next.
|
||||
}
|
||||
|
||||
// Push the nth argument.
|
||||
__ push(eax);
|
||||
|
||||
// Update the index on the stack and in register key.
|
||||
__ mov(key, Operand(ebp, kIndexOffset));
|
||||
__ add(key, Immediate(1 << kSmiTagSize));
|
||||
__ mov(Operand(ebp, kIndexOffset), key);
|
||||
|
||||
__ bind(&entry);
|
||||
__ cmp(key, Operand(ebp, kLimitOffset));
|
||||
__ j(not_equal, &loop);
|
||||
// Loop over the arguments array, pushing each value to the stack
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
// Call the function.
|
||||
Label call_proxy;
|
||||
ParameterCount actual(eax);
|
||||
__ Move(eax, key);
|
||||
__ SmiUntag(eax);
|
||||
__ mov(edi, Operand(ebp, kFunctionOffset));
|
||||
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
|
||||
__ j(not_equal, &call_proxy);
|
||||
__ InvokeFunction(edi, actual, CALL_FUNCTION, NullCallWrapper());
|
||||
|
||||
frame_scope.GenerateLeaveFrame();
|
||||
__ ret(3 * kPointerSize); // remove this, receiver, and arguments
|
||||
__ ret(kStackSize * kPointerSize); // remove this, receiver, and arguments
|
||||
|
||||
// Call the function proxy.
|
||||
__ bind(&call_proxy);
|
||||
@ -1149,7 +1188,92 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
|
||||
// Leave internal frame.
|
||||
}
|
||||
__ ret(3 * kPointerSize); // remove this, receiver, and arguments
|
||||
__ ret(kStackSize * kPointerSize); // remove this, receiver, and arguments
|
||||
}
|
||||
|
||||
|
||||
// Used by ReflectConstruct
|
||||
static void Generate_ConstructHelper(MacroAssembler* masm) {
|
||||
const int kFormalParameters = 3;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
// Stack at entry:
|
||||
// esp : return address
|
||||
// esp[4] : original constructor (new.target)
|
||||
// esp[8] : arguments
|
||||
// esp[16] : constructor
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Stack frame:
|
||||
// ebp : Old base pointer
|
||||
// ebp[4] : return address
|
||||
// ebp[8] : original constructor (new.target)
|
||||
// ebp[12] : arguments
|
||||
// ebp[16] : constructor
|
||||
static const int kNewTargetOffset = kFPOnStackSize + kPCOnStackSize;
|
||||
static const int kArgumentsOffset = kNewTargetOffset + kPointerSize;
|
||||
static const int kFunctionOffset = kArgumentsOffset + kPointerSize;
|
||||
|
||||
// If newTarget is not supplied, set it to constructor
|
||||
Label validate_arguments;
|
||||
__ mov(eax, Operand(ebp, kNewTargetOffset));
|
||||
__ CompareRoot(eax, Heap::kUndefinedValueRootIndex);
|
||||
__ j(not_equal, &validate_arguments, Label::kNear);
|
||||
__ mov(eax, Operand(ebp, kFunctionOffset));
|
||||
__ mov(Operand(ebp, kNewTargetOffset), eax);
|
||||
|
||||
// Validate arguments
|
||||
__ bind(&validate_arguments);
|
||||
__ push(Operand(ebp, kFunctionOffset));
|
||||
__ push(Operand(ebp, kArgumentsOffset));
|
||||
__ push(Operand(ebp, kNewTargetOffset));
|
||||
__ InvokeBuiltin(Builtins::REFLECT_CONSTRUCT_PREPARE, CALL_FUNCTION);
|
||||
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
// Push current index and limit.
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
|
||||
const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
|
||||
__ Push(eax); // limit
|
||||
__ push(Immediate(0)); // index
|
||||
// Push newTarget and callee functions
|
||||
__ push(Operand(ebp, kNewTargetOffset));
|
||||
__ push(Operand(ebp, kFunctionOffset));
|
||||
|
||||
// Loop over the arguments array, pushing each value to the stack
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
// Use undefined feedback vector
|
||||
__ LoadRoot(ebx, Heap::kUndefinedValueRootIndex);
|
||||
__ mov(edi, Operand(ebp, kFunctionOffset));
|
||||
|
||||
// Call the function.
|
||||
CallConstructStub stub(masm->isolate(), SUPER_CONSTRUCTOR_CALL);
|
||||
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
__ Drop(1);
|
||||
|
||||
// Leave internal frame.
|
||||
}
|
||||
// remove this, target, arguments, and newTarget
|
||||
__ ret(kStackSize * kPointerSize);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
Generate_ApplyHelper(masm, false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectApply(MacroAssembler* masm) {
|
||||
Generate_ApplyHelper(masm, true);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
|
||||
Generate_ConstructHelper(masm);
|
||||
}
|
||||
|
||||
|
||||
|
@ -69,6 +69,11 @@ const kMaxYear = 1000000;
|
||||
const kMinMonth = -10000000;
|
||||
const kMaxMonth = 10000000;
|
||||
|
||||
# Safe maximum number of arguments to push to stack, when multiplied by
|
||||
# pointer size. Used by Function.prototype.apply(), Reflect.apply() and
|
||||
# Reflect.construct().
|
||||
const kSafeArgumentsLength = 0x800000;
|
||||
|
||||
# Strict mode flags for passing to %SetProperty
|
||||
const kSloppyMode = 0;
|
||||
const kStrictMode = 1;
|
||||
|
@ -52,6 +52,8 @@ var kMessages = {
|
||||
no_setter_in_callback: ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"],
|
||||
apply_non_function: ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"],
|
||||
apply_wrong_args: ["Function.prototype.apply: Arguments list has wrong type"],
|
||||
reflect_apply_wrong_args: ["Reflect.apply: Arguments list has wrong type"],
|
||||
reflect_construct_wrong_args: ["Reflect.construct: Arguments list has wrong type"],
|
||||
flags_getter_non_object: ["RegExp.prototype.flags getter called on non-object ", "%0"],
|
||||
invalid_in_operator_use: ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"],
|
||||
instanceof_function_expected: ["Expecting a function in instanceof check, but got ", "%0"],
|
||||
|
@ -418,7 +418,7 @@ function APPLY_PREPARE(args) {
|
||||
// that takes care of more eventualities.
|
||||
if (IS_ARRAY(args)) {
|
||||
length = args.length;
|
||||
if (%_IsSmi(length) && length >= 0 && length < 0x800000 &&
|
||||
if (%_IsSmi(length) && length >= 0 && length < kSafeArgumentsLength &&
|
||||
IS_SPEC_FUNCTION(this)) {
|
||||
return length;
|
||||
}
|
||||
@ -429,7 +429,7 @@ function APPLY_PREPARE(args) {
|
||||
// We can handle any number of apply arguments if the stack is
|
||||
// big enough, but sanity check the value to avoid overflow when
|
||||
// multiplying with pointer size.
|
||||
if (length > 0x800000) {
|
||||
if (length > kSafeArgumentsLength) {
|
||||
throw %MakeRangeError('stack_overflow', []);
|
||||
}
|
||||
|
||||
@ -449,6 +449,93 @@ function APPLY_PREPARE(args) {
|
||||
}
|
||||
|
||||
|
||||
function REFLECT_APPLY_PREPARE(args) {
|
||||
var length;
|
||||
// First check whether length is a positive Smi and args is an
|
||||
// array. This is the fast case. If this fails, we do the slow case
|
||||
// that takes care of more eventualities.
|
||||
if (IS_ARRAY(args)) {
|
||||
length = args.length;
|
||||
if (%_IsSmi(length) && length >= 0 && length < kSafeArgumentsLength &&
|
||||
IS_SPEC_FUNCTION(this)) {
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_SPEC_FUNCTION(this)) {
|
||||
throw %MakeTypeError('called_non_callable', [ %ToString(this) ]);
|
||||
}
|
||||
|
||||
if (!IS_SPEC_OBJECT(args)) {
|
||||
throw %MakeTypeError('reflect_apply_wrong_args', [ ]);
|
||||
}
|
||||
|
||||
length = %ToLength(args.length);
|
||||
|
||||
// We can handle any number of apply arguments if the stack is
|
||||
// big enough, but sanity check the value to avoid overflow when
|
||||
// multiplying with pointer size.
|
||||
if (length > kSafeArgumentsLength) {
|
||||
throw %MakeRangeError('stack_overflow', []);
|
||||
}
|
||||
|
||||
// Return the length which is the number of arguments to copy to the
|
||||
// stack. It is guaranteed to be a small integer at this point.
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
function REFLECT_CONSTRUCT_PREPARE(args, newTarget) {
|
||||
var length;
|
||||
var ctorOk = IS_SPEC_FUNCTION(this) && %IsConstructor(this);
|
||||
var newTargetOk = IS_SPEC_FUNCTION(newTarget) && %IsConstructor(newTarget);
|
||||
|
||||
// First check whether length is a positive Smi and args is an
|
||||
// array. This is the fast case. If this fails, we do the slow case
|
||||
// that takes care of more eventualities.
|
||||
if (IS_ARRAY(args)) {
|
||||
length = args.length;
|
||||
if (%_IsSmi(length) && length >= 0 && length < kSafeArgumentsLength &&
|
||||
ctorOk && newTargetOk) {
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctorOk) {
|
||||
if (!IS_SPEC_FUNCTION(this)) {
|
||||
throw %MakeTypeError('called_non_callable', [ %ToString(this) ]);
|
||||
} else {
|
||||
throw %MakeTypeError('not_constructor', [ %ToString(this) ]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newTargetOk) {
|
||||
if (!IS_SPEC_FUNCTION(newTarget)) {
|
||||
throw %MakeTypeError('called_non_callable', [ %ToString(newTarget) ]);
|
||||
} else {
|
||||
throw %MakeTypeError('not_constructor', [ %ToString(newTarget) ]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_SPEC_OBJECT(args)) {
|
||||
throw %MakeTypeError('reflect_construct_wrong_args', [ ]);
|
||||
}
|
||||
|
||||
length = %ToLength(args.length);
|
||||
|
||||
// We can handle any number of apply arguments if the stack is
|
||||
// big enough, but sanity check the value to avoid overflow when
|
||||
// multiplying with pointer size.
|
||||
if (length > kSafeArgumentsLength) {
|
||||
throw %MakeRangeError('stack_overflow', []);
|
||||
}
|
||||
|
||||
// Return the length which is the number of arguments to copy to the
|
||||
// stack. It is guaranteed to be a small integer at this point.
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
function STACK_OVERFLOW(length) {
|
||||
throw %MakeRangeError('stack_overflow', []);
|
||||
}
|
||||
|
@ -1051,7 +1051,87 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
static void Generate_CheckStackOverflow(MacroAssembler* masm,
|
||||
const int calleeOffset) {
|
||||
// rax : the number of items to be pushed to the stack
|
||||
//
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
Label okay;
|
||||
__ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex);
|
||||
__ movp(rcx, rsp);
|
||||
// Make rcx the space we have left. The stack might already be overflowed
|
||||
// here which will cause rcx to become negative.
|
||||
__ subp(rcx, kScratchRegister);
|
||||
// Make rdx the space we need for the array when it is unrolled onto the
|
||||
// stack.
|
||||
__ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmpp(rcx, rdx);
|
||||
__ j(greater, &okay); // Signed comparison.
|
||||
|
||||
// Out of stack space.
|
||||
__ Push(Operand(rbp, calleeOffset));
|
||||
__ Push(rax);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
|
||||
__ bind(&okay);
|
||||
}
|
||||
|
||||
|
||||
static void Generate_PushAppliedArguments(MacroAssembler* masm,
|
||||
const int argumentsOffset,
|
||||
const int indexOffset,
|
||||
const int limitOffset) {
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
|
||||
// Copy all arguments from the array to the stack.
|
||||
Label entry, loop;
|
||||
__ movp(key, Operand(rbp, indexOffset));
|
||||
__ jmp(&entry);
|
||||
__ bind(&loop);
|
||||
__ movp(receiver, Operand(rbp, argumentsOffset)); // load arguments
|
||||
|
||||
// Use inline caching to speed up access to arguments.
|
||||
if (FLAG_vector_ics) {
|
||||
// TODO(mvstanton): Vector-based ics need additional infrastructure to
|
||||
// be embedded here. For now, just call the runtime.
|
||||
__ Push(receiver);
|
||||
__ Push(key);
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
} else {
|
||||
Handle<Code> ic = CodeFactory::KeyedLoadIC(masm->isolate()).code();
|
||||
__ Call(ic, RelocInfo::CODE_TARGET);
|
||||
// It is important that we do not have a test instruction after the
|
||||
// call. A test instruction after the call is used to indicate that
|
||||
// we have generated an inline version of the keyed load. In this
|
||||
// case, we know that we are not generating a test instruction next.
|
||||
}
|
||||
|
||||
// Push the nth argument.
|
||||
__ Push(rax);
|
||||
|
||||
// Update the index on the stack and in register key.
|
||||
__ movp(key, Operand(rbp, indexOffset));
|
||||
__ SmiAddConstant(key, key, Smi::FromInt(1));
|
||||
__ movp(Operand(rbp, indexOffset), key);
|
||||
|
||||
__ bind(&entry);
|
||||
__ cmpp(key, Operand(rbp, limitOffset));
|
||||
__ j(not_equal, &loop);
|
||||
|
||||
// On exit, the pushed arguments count is in rax, untagged
|
||||
__ SmiToInteger64(rax, key);
|
||||
}
|
||||
|
||||
|
||||
// Used by FunctionApply and ReflectApply
|
||||
static void Generate_ApplyHelper(MacroAssembler* masm, bool targetIsArgument) {
|
||||
const int kFormalParameters = targetIsArgument ? 3 : 2;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
// Stack at entry:
|
||||
// rsp : return address
|
||||
// rsp[8] : arguments
|
||||
@ -1071,30 +1151,13 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
|
||||
__ Push(Operand(rbp, kFunctionOffset));
|
||||
__ Push(Operand(rbp, kArgumentsOffset));
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
if (targetIsArgument) {
|
||||
__ InvokeBuiltin(Builtins::REFLECT_APPLY_PREPARE, CALL_FUNCTION);
|
||||
} else {
|
||||
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
|
||||
}
|
||||
|
||||
// Check the stack for overflow. We are not trying to catch
|
||||
// interruptions (e.g. debug break and preemption) here, so the "real stack
|
||||
// limit" is checked.
|
||||
Label okay;
|
||||
__ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex);
|
||||
__ movp(rcx, rsp);
|
||||
// Make rcx the space we have left. The stack might already be overflowed
|
||||
// here which will cause rcx to become negative.
|
||||
__ subp(rcx, kScratchRegister);
|
||||
// Make rdx the space we need for the array when it is unrolled onto the
|
||||
// stack.
|
||||
__ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2);
|
||||
// Check if the arguments will overflow the stack.
|
||||
__ cmpp(rcx, rdx);
|
||||
__ j(greater, &okay); // Signed comparison.
|
||||
|
||||
// Out of stack space.
|
||||
__ Push(Operand(rbp, kFunctionOffset));
|
||||
__ Push(rax);
|
||||
__ InvokeBuiltin(Builtins::STACK_OVERFLOW, CALL_FUNCTION);
|
||||
__ bind(&okay);
|
||||
// End of stack check.
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
// Push current index and limit.
|
||||
const int kLimitOffset =
|
||||
@ -1156,54 +1219,20 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
__ bind(&push_receiver);
|
||||
__ Push(rbx);
|
||||
|
||||
// Copy all arguments from the array to the stack.
|
||||
Label entry, loop;
|
||||
Register receiver = LoadDescriptor::ReceiverRegister();
|
||||
Register key = LoadDescriptor::NameRegister();
|
||||
__ movp(key, Operand(rbp, kIndexOffset));
|
||||
__ jmp(&entry);
|
||||
__ bind(&loop);
|
||||
__ movp(receiver, Operand(rbp, kArgumentsOffset)); // load arguments
|
||||
|
||||
// Use inline caching to speed up access to arguments.
|
||||
if (FLAG_vector_ics) {
|
||||
// TODO(mvstanton): Vector-based ics need additional infrastructure to
|
||||
// be embedded here. For now, just call the runtime.
|
||||
__ Push(receiver);
|
||||
__ Push(key);
|
||||
__ CallRuntime(Runtime::kGetProperty, 2);
|
||||
} else {
|
||||
Handle<Code> ic = CodeFactory::KeyedLoadIC(masm->isolate()).code();
|
||||
__ Call(ic, RelocInfo::CODE_TARGET);
|
||||
// It is important that we do not have a test instruction after the
|
||||
// call. A test instruction after the call is used to indicate that
|
||||
// we have generated an inline version of the keyed load. In this
|
||||
// case, we know that we are not generating a test instruction next.
|
||||
}
|
||||
|
||||
// Push the nth argument.
|
||||
__ Push(rax);
|
||||
|
||||
// Update the index on the stack and in register key.
|
||||
__ movp(key, Operand(rbp, kIndexOffset));
|
||||
__ SmiAddConstant(key, key, Smi::FromInt(1));
|
||||
__ movp(Operand(rbp, kIndexOffset), key);
|
||||
|
||||
__ bind(&entry);
|
||||
__ cmpp(key, Operand(rbp, kLimitOffset));
|
||||
__ j(not_equal, &loop);
|
||||
// Loop over the arguments array, pushing each value to the stack
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
// Call the function.
|
||||
Label call_proxy;
|
||||
ParameterCount actual(rax);
|
||||
__ SmiToInteger32(rax, key);
|
||||
__ movp(rdi, Operand(rbp, kFunctionOffset));
|
||||
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
|
||||
__ j(not_equal, &call_proxy);
|
||||
__ InvokeFunction(rdi, actual, CALL_FUNCTION, NullCallWrapper());
|
||||
|
||||
frame_scope.GenerateLeaveFrame();
|
||||
__ ret(3 * kPointerSize); // remove this, receiver, and arguments
|
||||
__ ret(kStackSize * kPointerSize); // remove this, receiver, and arguments
|
||||
|
||||
// Call the function proxy.
|
||||
__ bind(&call_proxy);
|
||||
@ -1216,7 +1245,92 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
|
||||
// Leave internal frame.
|
||||
}
|
||||
__ ret(3 * kPointerSize); // remove this, receiver, and arguments
|
||||
__ ret(kStackSize * kPointerSize); // remove this, receiver, and arguments
|
||||
}
|
||||
|
||||
|
||||
// Used by ReflectConstruct
|
||||
static void Generate_ConstructHelper(MacroAssembler* masm) {
|
||||
const int kFormalParameters = 3;
|
||||
const int kStackSize = kFormalParameters + 1;
|
||||
|
||||
// Stack at entry:
|
||||
// rsp : return address
|
||||
// rsp[8] : original constructor (new.target)
|
||||
// rsp[16] : arguments
|
||||
// rsp[24] : constructor
|
||||
{
|
||||
FrameScope frame_scope(masm, StackFrame::INTERNAL);
|
||||
// Stack frame:
|
||||
// rbp : Old base pointer
|
||||
// rbp[8] : return address
|
||||
// rbp[16] : original constructor (new.target)
|
||||
// rbp[24] : arguments
|
||||
// rbp[32] : constructor
|
||||
static const int kNewTargetOffset = kFPOnStackSize + kPCOnStackSize;
|
||||
static const int kArgumentsOffset = kNewTargetOffset + kPointerSize;
|
||||
static const int kFunctionOffset = kArgumentsOffset + kPointerSize;
|
||||
|
||||
// If newTarget is not supplied, set it to constructor
|
||||
Label validate_arguments;
|
||||
__ movp(rax, Operand(rbp, kNewTargetOffset));
|
||||
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
|
||||
__ j(not_equal, &validate_arguments, Label::kNear);
|
||||
__ movp(rax, Operand(rbp, kFunctionOffset));
|
||||
__ movp(Operand(rbp, kNewTargetOffset), rax);
|
||||
|
||||
// Validate arguments
|
||||
__ bind(&validate_arguments);
|
||||
__ Push(Operand(rbp, kFunctionOffset));
|
||||
__ Push(Operand(rbp, kArgumentsOffset));
|
||||
__ Push(Operand(rbp, kNewTargetOffset));
|
||||
__ InvokeBuiltin(Builtins::REFLECT_CONSTRUCT_PREPARE, CALL_FUNCTION);
|
||||
|
||||
Generate_CheckStackOverflow(masm, kFunctionOffset);
|
||||
|
||||
// Push current index and limit.
|
||||
const int kLimitOffset =
|
||||
StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
|
||||
const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
|
||||
__ Push(rax); // limit
|
||||
__ Push(Immediate(0)); // index
|
||||
// Push newTarget and callee functions
|
||||
__ Push(Operand(rbp, kNewTargetOffset));
|
||||
__ Push(Operand(rbp, kFunctionOffset));
|
||||
|
||||
// Loop over the arguments array, pushing each value to the stack
|
||||
Generate_PushAppliedArguments(
|
||||
masm, kArgumentsOffset, kIndexOffset, kLimitOffset);
|
||||
|
||||
// Use undefined feedback vector
|
||||
__ LoadRoot(rbx, Heap::kUndefinedValueRootIndex);
|
||||
__ movp(rdi, Operand(rbp, kFunctionOffset));
|
||||
|
||||
// Call the function.
|
||||
CallConstructStub stub(masm->isolate(), SUPER_CONSTRUCTOR_CALL);
|
||||
__ call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
|
||||
|
||||
__ Drop(1);
|
||||
|
||||
// Leave internal frame.
|
||||
}
|
||||
// remove this, target, arguments and newTarget
|
||||
__ ret(kStackSize * kPointerSize);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
|
||||
Generate_ApplyHelper(masm, false);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectApply(MacroAssembler* masm) {
|
||||
Generate_ApplyHelper(masm, true);
|
||||
}
|
||||
|
||||
|
||||
void Builtins::Generate_ReflectConstruct(MacroAssembler* masm) {
|
||||
Generate_ConstructHelper(masm);
|
||||
}
|
||||
|
||||
|
||||
|
212
test/mjsunit/harmony/reflect-apply.js
Normal file
212
test/mjsunit/harmony/reflect-apply.js
Normal file
@ -0,0 +1,212 @@
|
||||
// Copyright 2014 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: --harmony-reflect
|
||||
|
||||
|
||||
(function testReflectApplyArity() {
|
||||
assertEquals(3, Reflect.apply.length);
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectApplyNonConstructor() {
|
||||
assertThrows(function() {
|
||||
new Reflect.apply(function(){}, null, []);
|
||||
}, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedReceiverSloppy() {
|
||||
function returnThis() { return this; }
|
||||
var receiver = {};
|
||||
|
||||
assertSame(this, Reflect.apply(returnThis, void 0, []));
|
||||
assertSame(this, Reflect.apply(returnThis, null, []));
|
||||
assertSame(this, Reflect.apply(returnThis, this, []));
|
||||
assertSame(receiver, Reflect.apply(returnThis, receiver, []));
|
||||
|
||||
// Wrap JS values
|
||||
assertSame(String.prototype,
|
||||
Object.getPrototypeOf(Reflect.apply(returnThis, "str", [])));
|
||||
assertSame(Number.prototype,
|
||||
Object.getPrototypeOf(Reflect.apply(returnThis, 123, [])));
|
||||
assertSame(Boolean.prototype,
|
||||
Object.getPrototypeOf(Reflect.apply(returnThis, true, [])));
|
||||
assertSame(Symbol.prototype,
|
||||
Object.getPrototypeOf(
|
||||
Reflect.apply(returnThis, Symbol("test"), [])));
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedReceiverStrict() {
|
||||
function returnThis() { 'use strict'; return this; }
|
||||
var receiver = {};
|
||||
|
||||
assertSame(void 0, Reflect.apply(returnThis, void 0, []));
|
||||
assertSame(this, Reflect.apply(returnThis, this, []));
|
||||
assertSame(receiver, Reflect.apply(returnThis, receiver, []));
|
||||
|
||||
// Don't wrap value types
|
||||
var regexp = /123/;
|
||||
var symbol = Symbol("test");
|
||||
assertSame("str", Reflect.apply(returnThis, "str", []));
|
||||
assertSame(123, Reflect.apply(returnThis, 123, []));
|
||||
assertSame(true, Reflect.apply(returnThis, true, []));
|
||||
assertSame(regexp, Reflect.apply(returnThis, regexp, []));
|
||||
assertSame(symbol, Reflect.apply(returnThis, symbol, []));
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsLength() {
|
||||
function returnLengthStrict() { 'use strict'; return arguments.length; }
|
||||
function returnLengthSloppy() { return arguments.length; }
|
||||
|
||||
assertEquals(0, Reflect.apply(returnLengthStrict, this, []));
|
||||
assertEquals(0, Reflect.apply(returnLengthSloppy, this, []));
|
||||
assertEquals(0, Reflect.apply(returnLengthStrict, this, {}));
|
||||
assertEquals(0, Reflect.apply(returnLengthSloppy, this, {}));
|
||||
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
assertEquals(i, Reflect.apply(returnLengthStrict, this, new Array(i)));
|
||||
assertEquals(i, Reflect.apply(returnLengthSloppy, this, new Array(i)));
|
||||
assertEquals(i, Reflect.apply(returnLengthStrict, this, { length: i }));
|
||||
assertEquals(i, Reflect.apply(returnLengthSloppy, this, { length: i }));
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsLengthThrows() {
|
||||
function noopStrict() { 'use strict'; }
|
||||
function noopSloppy() { }
|
||||
function MyError() {}
|
||||
|
||||
var argsList = {};
|
||||
Object.defineProperty(argsList, "length", {
|
||||
get: function() { throw new MyError(); }
|
||||
});
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.apply(noopStrict, this, argsList);
|
||||
}, MyError);
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.apply(noopSloppy, this, argsList);
|
||||
}, MyError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsElementThrows() {
|
||||
function noopStrict() { 'use strict'; }
|
||||
function noopSloppy() { }
|
||||
function MyError() {}
|
||||
|
||||
var argsList = { length: 1 };
|
||||
Object.defineProperty(argsList, "0", {
|
||||
get: function() { throw new MyError(); }
|
||||
});
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.apply(noopStrict, this, argsList);
|
||||
}, MyError);
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.apply(noopSloppy, this, argsList);
|
||||
}, MyError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedNonFunctionStrict() {
|
||||
'use strict';
|
||||
assertThrows(function() { Reflect.apply(void 0); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(null); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(123); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply("str"); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(Symbol("x")); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(/123/); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(NaN); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply({}); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply([]); }, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedNonFunctionSloppy() {
|
||||
assertThrows(function() { Reflect.apply(void 0); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(null); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(123); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply("str"); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(Symbol("x")); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(/123/); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(NaN); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply({}); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply([]); }, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsNonList() {
|
||||
function noopStrict() { 'use strict'; }
|
||||
function noopSloppy() {}
|
||||
var R = void 0;
|
||||
assertThrows(function() { Reflect.apply(noopStrict, R, null); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopSloppy, R, null); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopStrict, R, 1); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopSloppy, R, 1); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopStrict, R, "BAD"); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopSloppy, R, "BAD"); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopStrict, R, true); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopSloppy, R, true); }, TypeError);
|
||||
var sym = Symbol("x");
|
||||
assertThrows(function() { Reflect.apply(noopStrict, R, sym); }, TypeError);
|
||||
assertThrows(function() { Reflect.apply(noopSloppy, R, sym); }, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentValue() {
|
||||
function returnFirstStrict(a) { 'use strict'; return a; }
|
||||
function returnFirstSloppy(a) { return a; }
|
||||
function returnLastStrict(a) {
|
||||
'use strict'; return arguments[arguments.length - 1]; }
|
||||
function returnLastSloppy(a) { return arguments[arguments.length - 1]; }
|
||||
function returnSumStrict() {
|
||||
'use strict';
|
||||
var sum = arguments[0];
|
||||
for (var i = 1; i < arguments.length; ++i) {
|
||||
sum += arguments[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
function returnSumSloppy() {
|
||||
var sum = arguments[0];
|
||||
for (var i = 1; i < arguments.length; ++i) {
|
||||
sum += arguments[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
assertEquals("OK!", Reflect.apply(returnFirstStrict, this, ["OK!"]));
|
||||
assertEquals("OK!", Reflect.apply(returnFirstSloppy, this, ["OK!"]));
|
||||
assertEquals("OK!", Reflect.apply(returnFirstStrict, this,
|
||||
{ 0: "OK!", length: 1 }));
|
||||
assertEquals("OK!", Reflect.apply(returnFirstSloppy, this,
|
||||
{ 0: "OK!", length: 1 }));
|
||||
assertEquals("OK!", Reflect.apply(returnLastStrict, this,
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, "OK!"]));
|
||||
assertEquals("OK!", Reflect.apply(returnLastSloppy, this,
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, "OK!"]));
|
||||
assertEquals("OK!", Reflect.apply(returnLastStrict, this,
|
||||
{ 9: "OK!", length: 10 }));
|
||||
assertEquals("OK!", Reflect.apply(returnLastSloppy, this,
|
||||
{ 9: "OK!", length: 10 }));
|
||||
assertEquals("TEST", Reflect.apply(returnSumStrict, this,
|
||||
["T", "E", "S", "T"]));
|
||||
assertEquals("TEST!!", Reflect.apply(returnSumStrict, this,
|
||||
["T", "E", "S", "T", "!", "!"]));
|
||||
assertEquals(10, Reflect.apply(returnSumStrict, this,
|
||||
{ 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }));
|
||||
assertEquals("TEST", Reflect.apply(returnSumSloppy, this,
|
||||
["T", "E", "S", "T"]));
|
||||
assertEquals("TEST!!", Reflect.apply(returnSumSloppy, this,
|
||||
["T", "E", "S", "T", "!", "!"]));
|
||||
assertEquals(10, Reflect.apply(returnSumSloppy, this,
|
||||
{ 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }));
|
||||
})();
|
277
test/mjsunit/harmony/reflect-construct.js
Normal file
277
test/mjsunit/harmony/reflect-construct.js
Normal file
@ -0,0 +1,277 @@
|
||||
// Copyright 2014 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: --harmony-reflect
|
||||
|
||||
|
||||
(function testReflectConstructArity() {
|
||||
assertEquals(2, Reflect.construct.length);
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructNonConstructor() {
|
||||
assertThrows(function() {
|
||||
new Reflect.construct(function(){}, []);
|
||||
}, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructBasic() {
|
||||
function Constructor() { "use strict"; }
|
||||
assertInstanceof(Reflect.construct(Constructor, []), Constructor);
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructBasicSloppy() {
|
||||
function Constructor() {}
|
||||
assertInstanceof(Reflect.construct(Constructor, []), Constructor);
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructReturnSomethingElseStrict() {
|
||||
var R = {};
|
||||
function Constructor() { "use strict"; return R; }
|
||||
assertSame(R, Reflect.construct(Constructor, []));
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructReturnSomethingElseSloppy() {
|
||||
var R = {};
|
||||
function Constructor() { return R; }
|
||||
assertSame(R, Reflect.construct(Constructor, []));
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructNewTargetStrict() {
|
||||
"use strict";
|
||||
function Constructor() { this[9] = 1; }
|
||||
var O = Reflect.construct(Constructor, [], Array);
|
||||
assertEquals(1, O[9]);
|
||||
// Ordinary object with Array.prototype --- no exotic Array magic
|
||||
assertFalse(Array.isArray(O));
|
||||
assertEquals(0, O.length);
|
||||
assertSame(Array.prototype, Object.getPrototypeOf(O));
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructNewTargetSloppy() {
|
||||
function Constructor() { this[9] = 1; }
|
||||
var O = Reflect.construct(Constructor, [], Array);
|
||||
assertEquals(1, O[9]);
|
||||
// Ordinary object with Array.prototype --- no exotic Array magic
|
||||
assertFalse(Array.isArray(O));
|
||||
assertEquals(0, O.length);
|
||||
assertSame(Array.prototype, Object.getPrototypeOf(O));
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructNewTargetStrict2() {
|
||||
"use strict";
|
||||
function Constructor() { this[9] = 1; }
|
||||
Constructor.prototype.add = function(x) {
|
||||
this[this.length] = x; return this;
|
||||
}
|
||||
var O = Reflect.construct(Array, [1, 2, 3], Constructor);
|
||||
// Exotic Array object with Constructor.prototype
|
||||
assertTrue(Array.isArray(O));
|
||||
assertSame(Constructor.prototype, Object.getPrototypeOf(O));
|
||||
assertFalse(O instanceof Array);
|
||||
assertEquals(3, O.length);
|
||||
assertEquals(undefined, O[9]);
|
||||
assertSame(O, O.add(4));
|
||||
assertEquals(4, O.length);
|
||||
assertEquals(4, O[3]);
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructNewTargetSloppy2() {
|
||||
function Constructor() { this[9] = 1; }
|
||||
Constructor.prototype.add = function(x) {
|
||||
this[this.length] = x; return this;
|
||||
}
|
||||
var O = Reflect.construct(Array, [1, 2, 3], Constructor);
|
||||
// Exotic Array object with Constructor.prototype
|
||||
assertTrue(Array.isArray(O));
|
||||
assertSame(Constructor.prototype, Object.getPrototypeOf(O));
|
||||
assertFalse(O instanceof Array);
|
||||
assertEquals(3, O.length);
|
||||
assertEquals(undefined, O[9]);
|
||||
assertSame(O, O.add(4));
|
||||
assertEquals(4, O.length);
|
||||
assertEquals(4, O[3]);
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructNewTargetStrict3() {
|
||||
"use strict";
|
||||
function A() {}
|
||||
function B() {}
|
||||
var O = Reflect.construct(A, [], B);
|
||||
// TODO(caitp): bug: newTarget prototype is not used if it is not
|
||||
// explicitly set.
|
||||
//assertSame(B.prototype, Object.getPrototypeOf(O));
|
||||
})();
|
||||
|
||||
|
||||
(function testReflectConstructNewTargetSloppy3() {
|
||||
function A() {}
|
||||
function B() {}
|
||||
var O = Reflect.construct(A, [], B);
|
||||
// TODO(caitp): bug: newTarget prototype is not used if it is not
|
||||
// explicitly set.
|
||||
//assertSame(B.prototype, Object.getPrototypeOf(O));
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsLength() {
|
||||
function lengthStrict() { 'use strict'; this.a = arguments.length; }
|
||||
function lengthSloppy() { this.a = arguments.length; }
|
||||
|
||||
assertEquals(0, Reflect.construct(lengthStrict, []).a);
|
||||
assertEquals(0, Reflect.construct(lengthSloppy, []).a);
|
||||
assertEquals(0, Reflect.construct(lengthStrict, {}).a);
|
||||
assertEquals(0, Reflect.construct(lengthSloppy, {}).a);
|
||||
|
||||
for (var i = 0; i < 256; ++i) {
|
||||
assertEquals(i, Reflect.construct(lengthStrict, new Array(i)).a);
|
||||
assertEquals(i, Reflect.construct(lengthSloppy, new Array(i)).a);
|
||||
assertEquals(i, Reflect.construct(lengthStrict, { length: i }).a);
|
||||
assertEquals(i, Reflect.construct(lengthSloppy, { length: i }).a);
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsLengthThrows() {
|
||||
function noopStrict() { 'use strict'; }
|
||||
function noopSloppy() { }
|
||||
function MyError() {}
|
||||
|
||||
var argsList = {};
|
||||
Object.defineProperty(argsList, "length", {
|
||||
get: function() { throw new MyError(); }
|
||||
});
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.construct(noopStrict, argsList);
|
||||
}, MyError);
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.construct(noopSloppy, argsList);
|
||||
}, MyError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsElementThrows() {
|
||||
function noopStrict() { 'use strict'; }
|
||||
function noopSloppy() { }
|
||||
function MyError() {}
|
||||
|
||||
var argsList = { length: 1 };
|
||||
Object.defineProperty(argsList, "0", {
|
||||
get: function() { throw new MyError(); }
|
||||
});
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.construct(noopStrict, argsList);
|
||||
}, MyError);
|
||||
|
||||
assertThrows(function() {
|
||||
Reflect.construct(noopSloppy, argsList);
|
||||
}, MyError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedNonFunctionStrict() {
|
||||
'use strict';
|
||||
assertThrows(function() { Reflect.construct(void 0, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(null, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(123, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct("str", []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(Symbol("x"), []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(/123/, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(NaN, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct({}, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct([], []); }, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedNonFunctionSloppy() {
|
||||
assertThrows(function() { Reflect.construct(void 0, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(null, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(123, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct("str", []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(Symbol("x"), []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(/123/, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(NaN, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct({}, []); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct([], []); }, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentsNonList() {
|
||||
function noopStrict() { 'use strict'; }
|
||||
function noopSloppy() {}
|
||||
assertThrows(function() { Reflect.construct(noopStrict, null); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopSloppy, null); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopStrict, 1); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopSloppy, 1); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopStrict, "BAD"); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopSloppy, "BAD"); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopStrict, true); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopSloppy, true); }, TypeError);
|
||||
var sym = Symbol("x");
|
||||
assertThrows(function() { Reflect.construct(noopStrict, sym); }, TypeError);
|
||||
assertThrows(function() { Reflect.construct(noopSloppy, sym); }, TypeError);
|
||||
})();
|
||||
|
||||
|
||||
(function testAppliedArgumentValue() {
|
||||
function firstStrict(a) { 'use strict'; this.a = a; }
|
||||
function firstSloppy(a) { this.a = a; }
|
||||
function lastStrict(a) {
|
||||
'use strict'; this.a = arguments[arguments.length - 1]; }
|
||||
function lastSloppy(a) { this.a = arguments[arguments.length - 1]; }
|
||||
function sumStrict() {
|
||||
'use strict';
|
||||
var sum = arguments[0];
|
||||
for (var i = 1; i < arguments.length; ++i) {
|
||||
sum += arguments[i];
|
||||
}
|
||||
this.a = sum;
|
||||
}
|
||||
function sumSloppy() {
|
||||
var sum = arguments[0];
|
||||
for (var i = 1; i < arguments.length; ++i) {
|
||||
sum += arguments[i];
|
||||
}
|
||||
this.a = sum;
|
||||
}
|
||||
|
||||
assertEquals("OK!", Reflect.construct(firstStrict, ["OK!"]).a);
|
||||
assertEquals("OK!", Reflect.construct(firstSloppy, ["OK!"]).a);
|
||||
assertEquals("OK!", Reflect.construct(firstStrict,
|
||||
{ 0: "OK!", length: 1 }).a);
|
||||
assertEquals("OK!", Reflect.construct(firstSloppy,
|
||||
{ 0: "OK!", length: 1 }).a);
|
||||
assertEquals("OK!", Reflect.construct(lastStrict,
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, "OK!"]).a);
|
||||
assertEquals("OK!", Reflect.construct(lastSloppy,
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, "OK!"]).a);
|
||||
assertEquals("OK!", Reflect.construct(lastStrict,
|
||||
{ 9: "OK!", length: 10 }).a);
|
||||
assertEquals("OK!", Reflect.construct(lastSloppy,
|
||||
{ 9: "OK!", length: 10 }).a);
|
||||
assertEquals("TEST", Reflect.construct(sumStrict,
|
||||
["T", "E", "S", "T"]).a);
|
||||
assertEquals("TEST!!", Reflect.construct(sumStrict,
|
||||
["T", "E", "S", "T", "!", "!"]).a);
|
||||
assertEquals(10, Reflect.construct(sumStrict,
|
||||
{ 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }).a);
|
||||
assertEquals("TEST", Reflect.construct(sumSloppy,
|
||||
["T", "E", "S", "T"]).a);
|
||||
assertEquals("TEST!!", Reflect.construct(sumSloppy,
|
||||
["T", "E", "S", "T", "!", "!"]).a);
|
||||
assertEquals(10, Reflect.construct(sumSloppy,
|
||||
{ 0: 1, 1: 2, 2: 3, 3: 4, length: 4 }).a);
|
||||
})();
|
@ -1713,7 +1713,8 @@
|
||||
'../../src/harmony-tostring.js',
|
||||
'../../src/harmony-typedarray.js',
|
||||
'../../src/harmony-templates.js',
|
||||
'../../src/harmony-regexp.js'
|
||||
'../../src/harmony-regexp.js',
|
||||
'../../src/harmony-reflect.js'
|
||||
],
|
||||
'libraries_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries.bin',
|
||||
'libraries_experimental_bin_file': '<(SHARED_INTERMEDIATE_DIR)/libraries-experimental.bin',
|
||||
|
Loading…
Reference in New Issue
Block a user