From d21fd15467e16f185e511dbfbaeef7caddfe804a Mon Sep 17 00:00:00 2001 From: caitpotter88 Date: Thu, 19 Mar 2015 07:47:18 -0700 Subject: [PATCH] [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} --- BUILD.gn | 1 + src/arm/builtins-arm.cc | 220 ++++++++++++----- src/arm64/builtins-arm64.cc | 239 ++++++++++++++----- src/bootstrapper.cc | 45 ++++ src/builtins.h | 6 + src/flag-definitions.h | 3 +- src/harmony-reflect.js | 18 ++ src/ia32/builtins-ia32.cc | 264 +++++++++++++++------ src/macros.py | 5 + src/messages.js | 2 + src/runtime.js | 91 ++++++- src/x64/builtins-x64.cc | 240 ++++++++++++++----- test/mjsunit/harmony/reflect-apply.js | 212 +++++++++++++++++ test/mjsunit/harmony/reflect-construct.js | 277 ++++++++++++++++++++++ tools/gyp/v8.gyp | 3 +- 15 files changed, 1367 insertions(+), 259 deletions(-) create mode 100644 src/harmony-reflect.js create mode 100644 test/mjsunit/harmony/reflect-apply.js create mode 100644 test/mjsunit/harmony/reflect-construct.js diff --git a/BUILD.gn b/BUILD.gn index e141f48a82..b7da6f9cba 100644 --- a/BUILD.gn +++ b/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 = [ diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 11645f4cc2..40531caf45 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -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 ------------- diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 89e304051a..a638d8dfbd 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -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 ------------- diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index d9a5af3a01..4cdf2d605c 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -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 builtins(native_context()->builtins()); + // Install references to functions of the Reflect object + { + Handle apply = + InstallFunction(builtins, "ReflectApply", JS_OBJECT_TYPE, + JSObject::kHeaderSize, MaybeHandle(), + Builtins::kReflectApply); + Handle construct = + InstallFunction(builtins, "ReflectConstruct", JS_OBJECT_TYPE, + JSObject::kHeaderSize, MaybeHandle(), + 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 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 global(JSGlobalObject::cast( + native_context()->global_object())); + Handle reflect_string = + factory()->NewStringFromStaticChars("Reflect"); + Handle reflect = + factory()->NewJSObject(isolate()->object_function(), TENURED); + JSObject::AddProperty(global, reflect_string, reflect, DONT_ENUM); +} + + Handle Genesis::InstallInternalArray( Handle 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++) { diff --git a/src/builtins.h b/src/builtins.h index cfbb77d7a3..c00a1a92c4 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -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); diff --git a/src/flag-definitions.h b/src/flag-definitions.h index c5c41c7248..c9ac7b57b7 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -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) \ diff --git a/src/harmony-reflect.js b/src/harmony-reflect.js new file mode 100644 index 0000000000..f900d70f29 --- /dev/null +++ b/src/harmony-reflect.js @@ -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(); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 9aa4e073f7..ea9b8c9704 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -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 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 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); } diff --git a/src/macros.py b/src/macros.py index c3a7888db0..324702bb3a 100644 --- a/src/macros.py +++ b/src/macros.py @@ -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; diff --git a/src/messages.js b/src/messages.js index 5c5e2075c1..8da7bb1bd3 100644 --- a/src/messages.js +++ b/src/messages.js @@ -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"], diff --git a/src/runtime.js b/src/runtime.js index fff0884df0..e4cb4ff312 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -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', []); } diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index f43084b13f..3141c17919 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -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 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 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); } diff --git a/test/mjsunit/harmony/reflect-apply.js b/test/mjsunit/harmony/reflect-apply.js new file mode 100644 index 0000000000..2cfb98282b --- /dev/null +++ b/test/mjsunit/harmony/reflect-apply.js @@ -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 })); +})(); diff --git a/test/mjsunit/harmony/reflect-construct.js b/test/mjsunit/harmony/reflect-construct.js new file mode 100644 index 0000000000..2211e3f783 --- /dev/null +++ b/test/mjsunit/harmony/reflect-construct.js @@ -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); +})(); diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp index a6ee03a556..dd0a8142ff 100644 --- a/tools/gyp/v8.gyp +++ b/tools/gyp/v8.gyp @@ -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',