Index: test/cctest/test-api.cc |
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc |
index 796127a438b17b294864b59099455d794c1c50c4..561fd4e397dfeeadebe2f77e87ea7fec05db89b0 100644 |
--- a/test/cctest/test-api.cc |
+++ b/test/cctest/test-api.cc |
@@ -21845,3 +21845,132 @@ TEST(Regress239669) { |
" new C1();" |
"}"); |
} |
+ |
+ |
+class ApiCallOptimizationChecker { |
+ private: |
+ static Local<Object> data; |
+ static Local<Object> receiver; |
+ static Local<Object> holder; |
+ static Local<Object> callee; |
+ static int count; |
+ |
+ static void OptimizationCallback( |
+ const v8::FunctionCallbackInfo<v8::Value>& info) { |
+ CHECK(callee == info.Callee()); |
+ CHECK(data == info.Data()); |
+ CHECK(receiver == info.This()); |
+ CHECK(holder == info.Holder()); |
+ count++; |
+ } |
+ |
+ public: |
+ void Run(bool use_signature, bool global) { |
+ v8::Isolate* isolate = CcTest::isolate(); |
+ v8::HandleScope scope(isolate); |
+ // Build a template for signature checks. |
+ Local<v8::ObjectTemplate> signature_template; |
+ Local<v8::Signature> signature; |
+ { |
+ Local<v8::FunctionTemplate> parent_template = |
+ FunctionTemplate::New(isolate); |
+ parent_template->SetHiddenPrototype(true); |
+ Local<v8::FunctionTemplate> function_template |
+ = FunctionTemplate::New(isolate); |
+ function_template->Inherit(parent_template); |
+ if (use_signature) { |
+ signature = v8::Signature::New(isolate, parent_template); |
+ } |
+ signature_template = function_template->InstanceTemplate(); |
+ } |
+ // Global object must pass checks. |
+ Local<v8::Context> context = |
+ v8::Context::New(isolate, NULL, signature_template); |
+ v8::Context::Scope context_scope(context); |
+ // Install regular object that can pass signature checks. |
+ Local<Object> function_receiver = signature_template->NewInstance(); |
+ context->Global()->Set(v8_str("function_receiver"), function_receiver); |
+ // Get the holder objects. |
+ Local<Object> inner_global = |
+ Local<Object>::Cast(context->Global()->GetPrototype()); |
+ Local<Object> function_holder = |
+ Local<Object>::Cast(function_receiver->GetPrototype()); |
+ // Install function on hidden prototype object. |
+ data = Object::New(isolate); |
+ Local<FunctionTemplate> function_template = FunctionTemplate::New( |
+ isolate, OptimizationCallback, data, signature); |
+ Local<Function> function = function_template->GetFunction(); |
+ Local<Object>::Cast( |
+ inner_global->GetPrototype())->Set(v8_str("global_f"), function); |
+ function_holder->Set(v8_str("f"), function); |
+ // Initialize expected values. |
+ callee = function; |
+ count = 0; |
+ if (global) { |
+ receiver = context->Global(); |
+ holder = inner_global; |
+ } else { |
+ holder = function_receiver; |
+ // If not using a signature, add something else to the prototype chain |
+ // to test the case that holder != receiver |
+ if (!use_signature) { |
+ receiver = Local<Object>::Cast(CompileRun( |
+ "var receiver_subclass = {};\n" |
+ "receiver_subclass.__proto__ = function_receiver;\n" |
+ "receiver_subclass")); |
+ } else { |
+ receiver = Local<Object>::Cast(CompileRun( |
+ "var receiver_subclass = function_receiver;\n" |
+ "receiver_subclass")); |
+ } |
+ } |
+ // With no signature, the holder is not set. |
+ if (!use_signature) holder = receiver; |
+ // build wrap_function |
+ int key = (use_signature ? 1 : 0) + 2 * (global ? 1 : 0); |
+ i::ScopedVector<char> wrap_function(100); |
+ if (global) { |
+ i::OS::SNPrintF( |
+ wrap_function, |
+ "function wrap_%d() { var f = global_f; return f(); }\n", |
+ key); |
+ } else { |
+ i::OS::SNPrintF( |
+ wrap_function, |
+ "function wrap_%d() { return receiver_subclass.f(); }\n", |
+ key); |
+ } |
+ // build source string |
+ i::ScopedVector<char> source(500); |
+ i::OS::SNPrintF( |
+ source, |
+ "%s\n" // wrap_function |
+ "function wrap2() { wrap_%d(); }\n" |
+ "wrap2();\n" |
+ "wrap2();\n" |
+ "%%OptimizeFunctionOnNextCall(wrap_%d);\n" |
+ "wrap2();\n", |
+ wrap_function.start(), key, key); |
+ v8::TryCatch try_catch; |
+ CompileRun(source.start()); |
+ ASSERT(!try_catch.HasCaught()); |
+ CHECK_EQ(3, count); |
+ } |
+}; |
+ |
+ |
+Local<Object> ApiCallOptimizationChecker::data; |
+Local<Object> ApiCallOptimizationChecker::receiver; |
+Local<Object> ApiCallOptimizationChecker::holder; |
+Local<Object> ApiCallOptimizationChecker::callee; |
+int ApiCallOptimizationChecker::count = 0; |
+ |
+ |
+TEST(TestFunctionCallOptimization) { |
+ i::FLAG_allow_natives_syntax = true; |
+ ApiCallOptimizationChecker checker; |
+ checker.Run(true, true); |
+ checker.Run(false, true); |
+ checker.Run(true, false); |
+ checker.Run(false, false); |
+} |