[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:
caitpotter88 2015-03-19 07:47:18 -07:00 committed by Commit bot
parent 1ecc161cc8
commit d21fd15467
15 changed files with 1367 additions and 259 deletions

View File

@ -266,6 +266,7 @@ action("js2c_experimental") {
"src/harmony-tostring.js",
"src/harmony-templates.js",
"src/harmony-regexp.js",
"src/harmony-reflect.js"
]
outputs = [

View File

@ -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 -------------

View File

@ -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 -------------

View File

@ -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++) {

View File

@ -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);

View File

@ -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
View 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();

View File

@ -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);
}

View File

@ -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;

View File

@ -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"],

View File

@ -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', []);
}

View File

@ -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);
}

View 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 }));
})();

View 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);
})();

View File

@ -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',