Index: test/cctest/test-api.cc |
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc |
index 38a9f45e2ff85a6d9b77210102b28a224732f755..6c0655a8f49eff801e5ba69e6e7a60205436d8ce 100644 |
--- a/test/cctest/test-api.cc |
+++ b/test/cctest/test-api.cc |
@@ -21736,10 +21736,6 @@ TEST(ScopedMicrotasks) { |
env->GetIsolate()->SetMicrotasksPolicy(v8::MicrotasksPolicy::kAuto); |
} |
-#ifdef ENABLE_DISASSEMBLER |
-// FLAG_test_primary_stub_cache and FLAG_test_secondary_stub_cache are read |
-// only when ENABLE_DISASSEMBLER is not defined. |
- |
namespace { |
int probes_counter = 0; |
@@ -21757,6 +21753,14 @@ int* LookupCounter(const char* name) { |
return NULL; |
} |
+} // namespace |
+ |
+#ifdef ENABLE_DISASSEMBLER |
+// FLAG_test_primary_stub_cache and FLAG_test_secondary_stub_cache are read |
+// only when ENABLE_DISASSEMBLER is not defined. |
+ |
+namespace { |
+ |
const char* kMegamorphicTestProgram = |
"function CreateClass(name) {\n" |
" var src = \n" |
@@ -22658,6 +22662,7 @@ TEST(AccessCheckThrows) { |
// Create a context and set an x property on it's global object. |
LocalContext context0(NULL, global_template); |
v8::Local<v8::Object> global0 = context0->Global(); |
+ CHECK(global0->Set(context0.local(), v8_str("x"), global0).FromJust()); |
// Create a context with a different security token so that the |
// failed access check callback will be called on each access. |
@@ -22712,6 +22717,128 @@ TEST(AccessCheckThrows) { |
isolate->SetFailedAccessCheckCallbackFunction(NULL); |
} |
+TEST(AccessCheckInIC) { |
+ // The test does not work with interpreter because bytecode handlers taken |
+ // from the snapshot already refer to ICs with disabled counters and there |
+ // is no way to trigger bytecode handlers recompilation. |
+ if (i::FLAG_ignition || i::FLAG_turbo) return; |
+ |
+ i::FLAG_native_code_counters = true; |
+ i::FLAG_crankshaft = false; |
+ i::FLAG_turbo = false; |
+ v8::Isolate::CreateParams create_params; |
+ create_params.array_buffer_allocator = CcTest::array_buffer_allocator(); |
+ create_params.counter_lookup_callback = LookupCounter; |
+ v8::Isolate* isolate = v8::Isolate::New(create_params); |
+ |
+ { |
+ v8::Isolate::Scope isolate_scope(isolate); |
+ LocalContext env(isolate); |
+ v8::HandleScope scope(isolate); |
+ |
+ { |
+ // Enforce recompilation of IC stubs that access megamorphic stub cache |
+ // to respect enabled native code counters and stub cache test flags. |
+ i::CodeStub::Major code_stub_keys[] = { |
+ i::CodeStub::LoadIC, i::CodeStub::LoadICTrampoline, |
+ i::CodeStub::KeyedLoadICTF, i::CodeStub::KeyedLoadICTrampolineTF, |
+ i::CodeStub::StoreIC, i::CodeStub::StoreICTrampoline, |
+ i::CodeStub::KeyedStoreIC, i::CodeStub::KeyedStoreICTrampoline, |
+ }; |
+ i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate); |
+ i::Heap* heap = i_isolate->heap(); |
+ i::Handle<i::UnseededNumberDictionary> dict(heap->code_stubs()); |
+ for (size_t i = 0; i < arraysize(code_stub_keys); i++) { |
+ dict = i::UnseededNumberDictionary::DeleteKey(dict, code_stub_keys[i]); |
+ } |
+ heap->SetRootCodeStubs(*dict); |
+ } |
+ |
+ // Create an ObjectTemplate for global objects and install access |
+ // check callbacks that will block access. |
+ v8::Local<v8::ObjectTemplate> global_template = |
+ v8::ObjectTemplate::New(isolate); |
+ global_template->SetAccessCheckCallback(AccessCounter); |
+ |
+ // Create a context and set an x property on its global object. |
+ LocalContext context0(isolate, NULL, global_template); |
+ v8::Local<v8::Object> global0 = context0->Global(); |
+ CHECK(global0->Set(context0.local(), v8_str("x"), global0).FromJust()); |
+ |
+ // Create a context with a different security token so that the |
+ // failed access check callback will be called on each access. |
+ LocalContext context1(isolate, NULL, global_template); |
+ CHECK(context1->Global() |
+ ->Set(context1.local(), v8_str("other"), global0) |
+ .FromJust()); |
+ |
+ // Set different security tokens. |
+ Local<Value> token0 = v8_str("token0"); |
+ context0.local()->SetSecurityToken(token0); |
+ context1.local()->SetSecurityToken(v8_str("token1")); |
+ |
+ int initial_probes = probes_counter; |
+ int initial_misses = misses_counter; |
+ int initial_updates = updates_counter; |
+ access_count = 0; |
+ |
+ // Create megamorphic load ic with a handler for "global0.x" compiled for |
+ // context0. |
+ CompileRun(context0.local(), |
+ "Number(1).__proto__.x = null;\n" |
+ "String(1).__proto__.x = null;\n" |
+ "function get0(o) { return o.x; };\n" |
+ "get0({x:1});\n" // premonomorphic |
+ "get0({x:1,a:0});\n" // monomorphic |
+ "get0({x:1,b:0});\n" // polymorphic |
+ "get0('str');\n" |
+ "get0(1.1);\n" |
+ "get0(this);\n" // megamorphic |
+ ""); |
+ CHECK_EQ(0, probes_counter - initial_probes); |
+ CHECK_EQ(0, misses_counter - initial_misses); |
+ CHECK_EQ(5, updates_counter - initial_updates); |
+ |
+ // Create megamorphic load ic in context1. |
+ CompileRun(context1.local(), |
+ "function get1(o) { return o.x; };\n" |
+ "get1({x:1});\n" // premonomorphic |
+ "get1({x:1,a:0});\n" // monomorphic |
+ "get1({x:1,b:0});\n" // polymorphic |
+ "get1({x:1,c:0});\n" |
+ "get1({x:1,d:0});\n" |
+ "get1({x:1,e:0});\n" // megamorphic |
+ ""); |
+ CHECK_EQ(0, access_count); |
+ CHECK_EQ(0, probes_counter - initial_probes); |
+ CHECK_EQ(0, misses_counter - initial_misses); |
+ CHECK_EQ(10, updates_counter - initial_updates); |
+ |
+ // Feed the |other| to the load ic and ensure that it doesn't pick the |
+ // handler for "global0.x" compiled for context0 from the megamorphic |
+ // cache but create another handler for "global0.x" compiled for context1 |
+ // and ensure the access check callback is triggered. |
+ CompileRun(context1.local(), "get1(other)"); |
+ CHECK_EQ(1, access_count); // Access check callback must be triggered. |
+ |
+ // Feed the primitive objects to the load ic and ensure that it doesn't |
+ // pick handlers for primitive maps from the megamorphic stub cache even |
+ // if the security token matches. |
+ context1.local()->SetSecurityToken(token0); |
+ CHECK(CompileRun(context1.local(), "get1(1.1)") |
+ .ToLocalChecked() |
+ ->IsUndefined()); |
+ CHECK(CompileRun(context1.local(), "get1('str')") |
+ .ToLocalChecked() |
+ ->IsUndefined()); |
+ |
+ CHECK_EQ(1, access_count); // Access check callback must be triggered. |
+ CHECK_EQ(3, probes_counter - initial_probes); |
+ CHECK_EQ(0, misses_counter - initial_misses); |
+ CHECK_EQ(13, updates_counter - initial_updates); |
+ } |
+ isolate->Dispose(); |
+} |
class RequestInterruptTestBase { |
public: |