}
+// Generate call to api function.
+static void GenerateFastApiCall(MacroAssembler* masm,
+ const CallOptimization& optimization,
+ Register receiver,
+ Register scratch,
+ int argc,
+ Register* values) {
+ ASSERT(optimization.is_simple_api_call());
+ ASSERT(!receiver.is(scratch));
+
+ const int stack_space = kFastApiCallArguments + argc + 1;
+ // Assign stack space for the call arguments.
+ __ sub(sp, sp, Operand(stack_space * kPointerSize));
+ // Write holder to stack frame.
+ __ str(receiver, MemOperand(sp, 0));
+ // Write receiver to stack frame.
+ int index = stack_space - 1;
+ __ str(receiver, MemOperand(sp, index * kPointerSize));
+ // Write the arguments to stack frame.
+ for (int i = 0; i < argc; i++) {
+ ASSERT(!receiver.is(values[i]));
+ ASSERT(!scratch.is(values[i]));
+ __ str(receiver, MemOperand(sp, index-- * kPointerSize));
+ }
+
+ GenerateFastApiDirectCall(masm, optimization, argc);
+}
+
+
class CallInterceptorCompiler BASE_EMBEDDED {
public:
CallInterceptorCompiler(StubCompiler* stub_compiler,
void BaseLoadStubCompiler::GenerateLoadCallback(
const CallOptimization& call_optimization) {
- ASSERT(call_optimization.is_simple_api_call());
-
- // Assign stack space for the call arguments.
- __ sub(sp, sp, Operand((kFastApiCallArguments + 1) * kPointerSize));
-
- int argc = 0;
- int api_call_argc = argc + kFastApiCallArguments;
- // Write holder to stack frame.
- __ str(receiver(), MemOperand(sp, 0));
- // Write receiver to stack frame.
- __ str(receiver(), MemOperand(sp, api_call_argc * kPointerSize));
-
- GenerateFastApiDirectCall(masm(), call_optimization, argc);
+ GenerateFastApiCall(
+ masm(), call_optimization, receiver(), scratch3(), 0, NULL);
}
}
+Handle<Code> StoreStubCompiler::CompileStoreCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ const CallOptimization& call_optimization) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+
+ Register values[] = { value() };
+ GenerateFastApiCall(
+ masm(), call_optimization, receiver(), scratch3(), 1, values);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
#undef __
#define __ ACCESS_MASM(masm)
}
+// Generate call to api function.
+static void GenerateFastApiCall(MacroAssembler* masm,
+ const CallOptimization& optimization,
+ Register receiver,
+ Register scratch,
+ int argc,
+ Register* values) {
+ ASSERT(optimization.is_simple_api_call());
+ ASSERT(!receiver.is(scratch));
+
+ const int stack_space = kFastApiCallArguments + argc + 1;
+ // Copy return value.
+ __ mov(scratch, Operand(esp, 0));
+ // Assign stack space for the call arguments.
+ __ sub(esp, Immediate(stack_space * kPointerSize));
+ // Move the return address on top of the stack.
+ __ mov(Operand(esp, 0), scratch);
+ // Write holder to stack frame.
+ __ mov(Operand(esp, 1 * kPointerSize), receiver);
+ // Write receiver to stack frame.
+ int index = stack_space;
+ __ mov(Operand(esp, index-- * kPointerSize), receiver);
+ // Write the arguments to stack frame.
+ for (int i = 0; i < argc; i++) {
+ ASSERT(!receiver.is(values[i]));
+ ASSERT(!scratch.is(values[i]));
+ __ mov(Operand(esp, index-- * kPointerSize), values[i]);
+ }
+
+ GenerateFastApiCall(masm, optimization, argc);
+}
+
+
class CallInterceptorCompiler BASE_EMBEDDED {
public:
CallInterceptorCompiler(StubCompiler* stub_compiler,
void BaseLoadStubCompiler::GenerateLoadCallback(
const CallOptimization& call_optimization) {
- ASSERT(call_optimization.is_simple_api_call());
-
- // Copy return value.
- __ mov(scratch3(), Operand(esp, 0));
- // Assign stack space for the call arguments.
- __ sub(esp, Immediate((kFastApiCallArguments + 1) * kPointerSize));
- // Move the return address on top of the stack.
- __ mov(Operand(esp, 0), scratch3());
-
- int argc = 0;
- int api_call_argc = argc + kFastApiCallArguments;
- // Write holder to stack frame.
- __ mov(Operand(esp, 1 * kPointerSize), receiver());
- // Write receiver to stack frame.
- __ mov(Operand(esp, (api_call_argc + 1) * kPointerSize), receiver());
-
- GenerateFastApiCall(masm(), call_optimization, argc);
+ GenerateFastApiCall(
+ masm(), call_optimization, receiver(), scratch3(), 0, NULL);
}
}
+Handle<Code> StoreStubCompiler::CompileStoreCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ const CallOptimization& call_optimization) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+
+ Register values[] = { value() };
+ GenerateFastApiCall(
+ masm(), call_optimization, receiver(), scratch1(), 1, values);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
#undef __
#define __ ACCESS_MASM(masm)
if (!setter->IsJSFunction()) break;
if (holder->IsGlobalObject()) break;
if (!holder->HasFastProperties()) break;
+ Handle<JSFunction> function = Handle<JSFunction>::cast(setter);
+ CallOptimization call_optimization(function);
+ if (call_optimization.is_simple_api_call() &&
+ call_optimization.IsCompatibleReceiver(*receiver)) {
+ return isolate()->stub_cache()->ComputeStoreCallback(
+ name, receiver, holder, call_optimization, strict_mode);
+ }
return isolate()->stub_cache()->ComputeStoreViaSetter(
name, receiver, holder, Handle<JSFunction>::cast(setter),
strict_mode);
}
+Handle<Code> StubCache::ComputeStoreCallback(
+ Handle<Name> name,
+ Handle<JSObject> receiver,
+ Handle<JSObject> holder,
+ const CallOptimization& call_optimization,
+ StrictModeFlag strict_mode) {
+ Handle<Code> stub = FindStoreHandler(
+ name, receiver, Code::STORE_IC, Code::CALLBACKS, strict_mode);
+ if (!stub.is_null()) return stub;
+
+ StoreStubCompiler compiler(isolate_, strict_mode);
+ Handle<Code> handler = compiler.CompileStoreCallback(
+ receiver, holder, name, call_optimization);
+ JSObject::UpdateMapCodeCache(receiver, name, handler);
+ return handler;
+}
+
+
Handle<Code> StubCache::ComputeStoreViaSetter(Handle<Name> name,
Handle<JSObject> receiver,
Handle<JSObject> holder,
Handle<ExecutableAccessorInfo> callback,
StrictModeFlag strict_mode);
+ Handle<Code> ComputeStoreCallback(Handle<Name> name,
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ const CallOptimization& call_optimation,
+ StrictModeFlag strict_mode);
+
Handle<Code> ComputeStoreViaSetter(Handle<Name> name,
Handle<JSObject> object,
Handle<JSObject> holder,
Handle<Name> name,
Handle<ExecutableAccessorInfo> callback);
+ Handle<Code> CompileStoreCallback(Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ const CallOptimization& call_optimization);
+
static void GenerateStoreViaSetter(MacroAssembler* masm,
Handle<JSFunction> setter);
}
+// Generate call to api function.
+static void GenerateFastApiCall(MacroAssembler* masm,
+ const CallOptimization& optimization,
+ Register receiver,
+ Register scratch,
+ int argc,
+ Register* values) {
+ ASSERT(optimization.is_simple_api_call());
+ ASSERT(!receiver.is(scratch));
+
+ const int stack_space = kFastApiCallArguments + argc + 1;
+ // Copy return value.
+ __ movq(scratch, Operand(rsp, 0));
+ // Assign stack space for the call arguments.
+ __ subq(rsp, Immediate(stack_space * kPointerSize));
+ // Move the return address on top of the stack.
+ __ movq(Operand(rsp, 0), scratch);
+ // Write holder to stack frame.
+ __ movq(Operand(rsp, 1 * kPointerSize), receiver);
+ // Write receiver to stack frame.
+ int index = stack_space;
+ __ movq(Operand(rsp, index-- * kPointerSize), receiver);
+ // Write the arguments to stack frame.
+ for (int i = 0; i < argc; i++) {
+ ASSERT(!receiver.is(values[i]));
+ ASSERT(!scratch.is(values[i]));
+ __ movq(Operand(rsp, index-- * kPointerSize), values[i]);
+ }
+
+ GenerateFastApiCall(masm, optimization, argc);
+}
+
+
class CallInterceptorCompiler BASE_EMBEDDED {
public:
CallInterceptorCompiler(StubCompiler* stub_compiler,
void BaseLoadStubCompiler::GenerateLoadCallback(
const CallOptimization& call_optimization) {
- ASSERT(call_optimization.is_simple_api_call());
-
- // Copy return value.
- __ movq(scratch3(), Operand(rsp, 0));
- // Assign stack space for the call arguments.
- __ subq(rsp, Immediate((kFastApiCallArguments + 1) * kPointerSize));
- // Move the return address on top of the stack.
- __ movq(Operand(rsp, 0), scratch3());
-
- int argc = 0;
- int api_call_argc = argc + kFastApiCallArguments;
- StackArgumentsAccessor args(rsp, api_call_argc);
- // Write holder to stack frame.
- __ movq(args.GetArgumentOperand(api_call_argc), receiver());
- // Write receiver to stack frame.
- __ movq(args.GetArgumentOperand(api_call_argc - 6), receiver());
-
- GenerateFastApiCall(masm(), call_optimization, argc);
+ GenerateFastApiCall(
+ masm(), call_optimization, receiver(), scratch3(), 0, NULL);
}
}
+Handle<Code> StoreStubCompiler::CompileStoreCallback(
+ Handle<JSObject> object,
+ Handle<JSObject> holder,
+ Handle<Name> name,
+ const CallOptimization& call_optimization) {
+ Label success;
+ HandlerFrontend(object, receiver(), holder, name, &success);
+ __ bind(&success);
+
+ Register values[] = { value() };
+ GenerateFastApiCall(
+ masm(), call_optimization, receiver(), scratch3(), 1, values);
+
+ // Return the generated code.
+ return GetCode(kind(), Code::CALLBACKS, name);
+}
+
+
#undef __
#define __ ACCESS_MASM(masm)
}
+static void TestSignature(const char* loop_js, Local<Value> receiver) {
+ i::ScopedVector<char> source(200);
+ i::OS::SNPrintF(source,
+ "for (var i = 0; i < 10; i++) {"
+ " %s"
+ "}",
+ loop_js);
+ signature_callback_count = 0;
+ signature_expected_receiver = receiver;
+ bool expected_to_throw = receiver.IsEmpty();
+ v8::TryCatch try_catch;
+ CompileRun(source.start());
+ CHECK_EQ(expected_to_throw, try_catch.HasCaught());
+ if (!expected_to_throw) {
+ CHECK_EQ(10, signature_callback_count);
+ } else {
+ CHECK_EQ(v8_str("TypeError: Illegal invocation"),
+ try_catch.Exception()->ToString());
+ }
+}
+
+
THREADED_TEST(ReceiverSignature) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
+ // Setup templates.
v8::Handle<v8::FunctionTemplate> fun = v8::FunctionTemplate::New();
v8::Handle<v8::Signature> sig = v8::Signature::New(fun);
- fun->PrototypeTemplate()->Set(
- v8_str("m"),
- v8::FunctionTemplate::New(IncrementingSignatureCallback,
- v8::Handle<Value>(),
- sig));
- fun->PrototypeTemplate()->SetAccessorProperty(
- v8_str("n"),
- v8::FunctionTemplate::New(IncrementingSignatureCallback,
- v8::Handle<Value>(),
- sig));
- env->Global()->Set(v8_str("Fun"), fun->GetFunction());
- Local<Value> fun_instance = fun->InstanceTemplate()->NewInstance();
- env->Global()->Set(v8_str("fun_instance"), fun_instance);
- signature_callback_count = 0;
- int expected_count = 0;
- signature_expected_receiver = fun_instance;
- CompileRun(
- "var o = fun_instance;"
- "var key_n = 'n';"
- "for (var i = 0; i < 10; i++) o.m();"
- "for (var i = 0; i < 10; i++) o.n;"
- "for (var i = 0; i < 10; i++) o[key_n];");
- expected_count += 30;
- CHECK_EQ(expected_count, signature_callback_count);
+ v8::Handle<v8::FunctionTemplate> callback_sig =
+ v8::FunctionTemplate::New(
+ IncrementingSignatureCallback, Local<Value>(), sig);
+ v8::Handle<v8::FunctionTemplate> callback =
+ v8::FunctionTemplate::New(IncrementingSignatureCallback);
v8::Handle<v8::FunctionTemplate> sub_fun = v8::FunctionTemplate::New();
sub_fun->Inherit(fun);
- fun_instance = sub_fun->InstanceTemplate()->NewInstance();
- env->Global()->Set(v8_str("fun_instance"), fun_instance);
- signature_expected_receiver = fun_instance;
- CompileRun(
- "var o = fun_instance;"
- "var key_n = 'n';"
- "for (var i = 0; i < 10; i++) o.m();"
- "for (var i = 0; i < 10; i++) o.n;"
- "for (var i = 0; i < 10; i++) o[key_n];");
- expected_count += 30;
- CHECK_EQ(expected_count, signature_callback_count);
- v8::TryCatch try_catch;
- CompileRun(
- "var o = { };"
- "o.m = Fun.prototype.m;"
- "o.m();");
- CHECK_EQ(expected_count, signature_callback_count);
- CHECK(try_catch.HasCaught());
- CompileRun(
- "var o = { };"
- "o.n = Fun.prototype.n;"
- "o.n;");
- CHECK_EQ(expected_count, signature_callback_count);
- CHECK(try_catch.HasCaught());
- try_catch.Reset();
v8::Handle<v8::FunctionTemplate> unrel_fun = v8::FunctionTemplate::New();
- sub_fun->Inherit(fun);
+ // Install properties.
+ v8::Handle<v8::ObjectTemplate> fun_proto = fun->PrototypeTemplate();
+ fun_proto->Set(v8_str("prop_sig"), callback_sig);
+ fun_proto->Set(v8_str("prop"), callback);
+ fun_proto->SetAccessorProperty(
+ v8_str("accessor_sig"), callback_sig, callback_sig);
+ fun_proto->SetAccessorProperty(v8_str("accessor"), callback, callback);
+ // Instantiate templates.
+ Local<Value> fun_instance = fun->InstanceTemplate()->NewInstance();
+ Local<Value> sub_fun_instance = sub_fun->InstanceTemplate()->NewInstance();
+ // Setup global variables.
+ env->Global()->Set(v8_str("Fun"), fun->GetFunction());
env->Global()->Set(v8_str("UnrelFun"), unrel_fun->GetFunction());
+ env->Global()->Set(v8_str("fun_instance"), fun_instance);
+ env->Global()->Set(v8_str("sub_fun_instance"), sub_fun_instance);
CompileRun(
- "var o = new UnrelFun();"
- "o.m = Fun.prototype.m;"
- "o.m();");
- CHECK_EQ(expected_count, signature_callback_count);
- CHECK(try_catch.HasCaught());
- try_catch.Reset();
- CompileRun(
- "var o = new UnrelFun();"
- "o.n = Fun.prototype.n;"
- "o.n;");
- CHECK_EQ(expected_count, signature_callback_count);
- CHECK(try_catch.HasCaught());
+ "var accessor_sig_key = 'accessor_sig';"
+ "var accessor_key = 'accessor';"
+ "var prop_sig_key = 'prop_sig';"
+ "var prop_key = 'prop';"
+ ""
+ "function copy_props(obj) {"
+ " var keys = [accessor_sig_key, accessor_key, prop_sig_key, prop_key];"
+ " var source = Fun.prototype;"
+ " for (var i in keys) {"
+ " var key = keys[i];"
+ " var desc = Object.getOwnPropertyDescriptor(source, key);"
+ " Object.defineProperty(obj, key, desc);"
+ " }"
+ "}"
+ ""
+ "var obj = {};"
+ "copy_props(obj);"
+ "var unrel = new UnrelFun();"
+ "copy_props(unrel);");
+ // Test with and without ICs
+ const char* test_objects[] = {
+ "fun_instance", "sub_fun_instance", "obj", "unrel" };
+ unsigned bad_signature_start_offset = 2;
+ for (unsigned i = 0; i < ARRAY_SIZE(test_objects); i++) {
+ i::ScopedVector<char> source(200);
+ i::OS::SNPrintF(
+ source, "var test_object = %s; test_object", test_objects[i]);
+ Local<Value> test_object = CompileRun(source.start());
+ TestSignature("test_object.prop();", test_object);
+ TestSignature("test_object.accessor;", test_object);
+ TestSignature("test_object[accessor_key];", test_object);
+ TestSignature("test_object.accessor = 1;", test_object);
+ TestSignature("test_object[accessor_key] = 1;", test_object);
+ if (i >= bad_signature_start_offset) test_object = Local<Value>();
+ TestSignature("test_object.prop_sig();", test_object);
+ TestSignature("test_object.accessor_sig;", test_object);
+ TestSignature("test_object[accessor_sig_key];", test_object);
+ TestSignature("test_object.accessor_sig = 1;", test_object);
+ TestSignature("test_object[accessor_sig_key] = 1;", test_object);
+ }
}