Make sure that the generic stubs for keyed load and store and for
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 10 Dec 2008 08:05:10 +0000 (08:05 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 10 Dec 2008 08:05:10 +0000 (08:05 +0000)
dictionary probing respects access check bit.
Review URL: http://codereview.chromium.org/13663

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@955 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/ic-arm.cc
src/ic-ia32.cc
src/runtime.cc
test/cctest/test-api.cc

index dc352561e932f50f16df66fa594c4cc2b4c0c458..55bbee489797fa5bdf44439c456120997a1064fe 100644 (file)
@@ -317,8 +317,8 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
   __ b(eq, &miss);
 
   // Check that the receiver is a valid JS object.
-  __ ldr(r0, FieldMemOperand(r1, HeapObject::kMapOffset));
-  __ ldrb(r0, FieldMemOperand(r0, Map::kInstanceTypeOffset));
+  __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
+  __ ldrb(r0, FieldMemOperand(r3, Map::kInstanceTypeOffset));
   __ cmp(r0, Operand(FIRST_JS_OBJECT_TYPE));
   __ b(lt, &miss);
 
@@ -333,6 +333,10 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
 
   // Accessing global object: Load and invoke.
   __ bind(&global_object);
+  // Check that the global object does not require access checks.
+  __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
+  __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded));
+  __ b(ne, &miss);
   GenerateNormalHelper(masm, argc, true, &miss);
 
   // Accessing non-global object: Check for access to global proxy.
@@ -340,6 +344,11 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
   __ bind(&non_global_object);
   __ cmp(r0, Operand(JS_GLOBAL_PROXY_TYPE));
   __ b(eq, &global_proxy);
+  // Check that the non-global, non-global-proxy object does not
+  // require access checks.
+  __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
+  __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded));
+  __ b(ne, &miss);
   __ bind(&invoke);
   GenerateNormalHelper(masm, argc, false, &miss);
 
@@ -441,8 +450,8 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
   __ b(eq, &miss);
 
   // Check that the receiver is a valid JS object.
-  __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
-  __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
+  __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset));
+  __ ldrb(r1, FieldMemOperand(r3, Map::kInstanceTypeOffset));
   __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE));
   __ b(lt, &miss);
   // If this assert fails, we have to check upper bound too.
@@ -452,6 +461,11 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
   __ cmp(r1, Operand(JS_GLOBAL_PROXY_TYPE));
   __ b(eq, &global);
 
+  // Check for non-global object that requires access check.
+  __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
+  __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded));
+  __ b(ne, &miss);
+
   __ bind(&probe);
   GenerateDictionaryLoad(masm, &miss, r1, r0);
   GenerateCheckNonFunctionOrLoaded(masm, &miss, r0, r1);
@@ -525,12 +539,19 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
   __ tst(r1, Operand(kSmiTagMask));
   __ b(eq, &slow);
 
+  // Get the map of the receiver.
+  __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+  // Check that the receiver does not require access checks.  We need
+  // to check this explicitly since this generic stub does not perform
+  // map checks.
+  __ ldrb(r3, FieldMemOperand(r2, Map::kBitFieldOffset));
+  __ tst(r3, Operand(1 << Map::kIsAccessCheckNeeded));
+  __ b(ne, &slow);
   // Check that the object is some kind of JS object EXCEPT JS Value type.
   // In the case that the object is a value-wrapper object,
   // we enter the runtime system to make sure that indexing into string
   // objects work as intended.
   ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
-  __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
   __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
   __ cmp(r2, Operand(JS_OBJECT_TYPE));
   __ b(lt, &slow);
@@ -597,10 +618,15 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   // Check that the object isn't a smi.
   __ tst(r3, Operand(kSmiTagMask));
   __ b(eq, &slow);
-  // Get the type of the object from its map.
+  // Get the map of the object.
   __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
-  __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
+  // Check that the receiver does not require access checks.  We need
+  // to do this because this generic stub does not perform map checks.
+  __ ldrb(ip, FieldMemOperand(r2, Map::kBitFieldOffset));
+  __ tst(ip, Operand(1 << Map::kIsAccessCheckNeeded));
+  __ b(ne, &slow);
   // Check if the object is a JS array or not.
+  __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
   __ cmp(r2, Operand(JS_ARRAY_TYPE));
   // r1 == key.
   __ b(eq, &array);
index 7ff47b9902be24a3552e4b7ec7f411205a72fd47..6021004ddfff258b0e90d6683c50813d87a30a51 100644 (file)
@@ -215,18 +215,27 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
   // -----------------------------------
   Label slow, fast, check_string, index_int, index_string;
 
+  // Load name and receiver.
   __ mov(eax, (Operand(esp, kPointerSize)));
   __ mov(ecx, (Operand(esp, 2 * kPointerSize)));
 
   // Check that the object isn't a smi.
   __ test(ecx, Immediate(kSmiTagMask));
   __ j(zero, &slow, not_taken);
+
+  // Get the map of the receiver.
+  __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset));
+  // Check that the receiver does not require access checks.  We need
+  // to check this explicitly since this generic stub does not perform
+  // map checks.
+  __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset));
+  __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
+  __ j(not_zero, &slow, not_taken);
   // Check that the object is some kind of JS object EXCEPT JS Value type.
   // In the case that the object is a value-wrapper object,
   // we enter the runtime system to make sure that indexing
   // into string objects work as intended.
   ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
-  __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset));
   __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset));
   __ cmp(edx, JS_OBJECT_TYPE);
   __ j(less, &slow, not_taken);
@@ -268,7 +277,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
   // bits have been subtracted to allow space for the length and the cached
   // array index.
   ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
-             (1 << (String::kShortLengthShift - String::kHashShift)));
+         (1 << (String::kShortLengthShift - String::kHashShift)));
   __ bind(&index_string);
   const int kLengthFieldLimit =
       (String::kMaxCachedArrayIndexLength + 1) << String::kShortLengthShift;
@@ -298,17 +307,25 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   //  -- esp[8] : receiver
   // -----------------------------------
   Label slow, fast, array, extra;
-  // Get the key and the object from the stack.
-  __ mov(ebx, Operand(esp, 1 * kPointerSize));  // 1 ~ return address
+
+  // Get the receiver from the stack.
   __ mov(edx, Operand(esp, 2 * kPointerSize));  // 2 ~ return address, key
-  // Check that the key is a smi.
-  __ test(ebx, Immediate(kSmiTagMask));
-  __ j(not_zero, &slow, not_taken);
   // Check that the object isn't a smi.
   __ test(edx, Immediate(kSmiTagMask));
   __ j(zero, &slow, not_taken);
-  // Get the type of the object from its map.
+  // Get the map from the receiver.
   __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+  // Check that the receiver does not require access checks.  We need
+  // to do this because this generic stub does not perform map checks.
+  __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
+  __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
+  __ j(not_zero, &slow, not_taken);
+  // Get the key from the stack.
+  __ mov(ebx, Operand(esp, 1 * kPointerSize));  // 1 ~ return address
+  // Check that the key is a smi.
+  __ test(ebx, Immediate(kSmiTagMask));
+  __ j(not_zero, &slow, not_taken);
+  // Get the instance type from the map of the receiver.
   __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
   // Check if the object is a JS array or not.
   __ cmp(ecx, JS_ARRAY_TYPE);
@@ -317,7 +334,6 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
   __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
   __ j(less, &slow, not_taken);
 
-
   // Object case: Check key against length in the elements array.
   // eax: value
   // edx: JSObject
@@ -515,8 +531,8 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
   __ j(zero, &miss, not_taken);
 
   // Check that the receiver is a valid JS object.
-  __ mov(eax, FieldOperand(edx, HeapObject::kMapOffset));
-  __ movzx_b(eax, FieldOperand(eax, Map::kInstanceTypeOffset));
+  __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
+  __ movzx_b(eax, FieldOperand(ebx, Map::kInstanceTypeOffset));
   __ cmp(eax, FIRST_JS_OBJECT_TYPE);
   __ j(less, &miss, not_taken);
 
@@ -531,6 +547,10 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
 
   // Accessing global object: Load and invoke.
   __ bind(&global_object);
+  // Check that the global object does not require access checks.
+  __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset));
+  __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
+  __ j(not_equal, &miss, not_taken);
   GenerateNormalHelper(masm, argc, true, &miss);
 
   // Accessing non-global object: Check for access to global proxy.
@@ -538,6 +558,11 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
   __ bind(&non_global_object);
   __ cmp(eax, JS_GLOBAL_PROXY_TYPE);
   __ j(equal, &global_proxy, not_taken);
+  // Check that the non-global, non-global-proxy object does not
+  // require access checks.
+  __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset));
+  __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
+  __ j(not_equal, &miss, not_taken);
   __ bind(&invoke);
   GenerateNormalHelper(masm, argc, false, &miss);
 
@@ -642,8 +667,8 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
   __ j(zero, &miss, not_taken);
 
   // Check that the receiver is a valid JS object.
-  __ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
-  __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset));
+  __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+  __ movzx_b(edx, FieldOperand(ebx, Map::kInstanceTypeOffset));
   __ cmp(edx, FIRST_JS_OBJECT_TYPE);
   __ j(less, &miss, not_taken);
 
@@ -654,6 +679,11 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
   __ cmp(edx, JS_GLOBAL_PROXY_TYPE);
   __ j(equal, &global, not_taken);
 
+  // Check for non-global object that requires access check.
+  __ movzx_b(ebx, FieldOperand(ebx, Map::kBitFieldOffset));
+  __ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
+  __ j(not_zero, &miss, not_taken);
+
   // Search the dictionary placing the result in eax.
   __ bind(&probe);
   GenerateDictionaryLoad(masm, &miss, edx, eax, ebx, ecx);
index 749bee087774d770f10f7dc92cde8ec03f785383..c9277b26b930b672682822e0e6da4e642dcebecf 100644 (file)
@@ -1733,13 +1733,19 @@ static Object* Runtime_KeyedGetProperty(Arguments args) {
   ASSERT(args.length() == 2);
 
   // Fast cases for getting named properties of the receiver JSObject
-  // itself. The global proxy objects has to be excluded since
-  // LocalLookup on the global proxy object can return a valid result
-  // eventhough the global proxy object never has properties.  This is
-  // the case because the global proxy object forwards everything to
-  // its hidden prototype including local lookups.
+  // itself.
+  //
+  // The global proxy objects has to be excluded since LocalLookup on
+  // the global proxy object can return a valid result eventhough the
+  // global proxy object never has properties.  This is the case
+  // because the global proxy object forwards everything to its hidden
+  // prototype including local lookups.
+  //
+  // Additionally, we need to make sure that we do not cache results
+  // for objects that require access checks.
   if (args[0]->IsJSObject() &&
       !args[0]->IsJSGlobalProxy() &&
+      !args[0]->IsAccessCheckNeeded() &&
       args[1]->IsString()) {
     JSObject* receiver = JSObject::cast(args[0]);
     String* key = String::cast(args[1]);
index c930a1efc8ee02a8b2b1733645865894849c99ab..5c355de82979ad55bd6b9021384bb3427a01c6be 100644 (file)
@@ -3681,29 +3681,81 @@ TEST(AccessControlIC) {
   v8::Handle<Value> value;
 
   // Check that the named access-control function is called every time.
-  value = v8_compile("for (var i = 0; i < 10; i++)  obj.prop = 1;")->Run();
-  value = v8_compile("for (var i = 0; i < 10; i++)  obj.prop;"
-                     "obj.prop")->Run();
+  CompileRun("function testProp(obj) {"
+             "  for (var i = 0; i < 10; i++) obj.prop = 1;"
+             "  for (var j = 0; j < 10; j++) obj.prop;"
+             "  return obj.prop"
+             "}");
+  value = CompileRun("testProp(obj)");
   CHECK(value->IsNumber());
   CHECK_EQ(1, value->Int32Value());
   CHECK_EQ(21, named_access_count);
 
   // Check that the named access-control function is called every time.
-  value = v8_compile("var p = 'prop';")->Run();
-  value = v8_compile("for (var i = 0; i < 10; i++) obj[p] = 1;")->Run();
-  value = v8_compile("for (var i = 0; i < 10; i++) obj[p];"
-                     "obj[p]")->Run();
+  CompileRun("var p = 'prop';"
+             "function testKeyed(obj) {"
+             "  for (var i = 0; i < 10; i++) obj[p] = 1;"
+             "  for (var j = 0; j < 10; j++) obj[p];"
+             "  return obj[p];"
+             "}");
+  // Use obj which requires access checks.  No inline caching is used
+  // in that case.
+  value = CompileRun("testKeyed(obj)");
   CHECK(value->IsNumber());
   CHECK_EQ(1, value->Int32Value());
   CHECK_EQ(42, named_access_count);
+  // Force the inline caches into generic state and try again.
+  CompileRun("testKeyed({ a: 0 })");
+  CompileRun("testKeyed({ b: 0 })");
+  value = CompileRun("testKeyed(obj)");
+  CHECK(value->IsNumber());
+  CHECK_EQ(1, value->Int32Value());
+  CHECK_EQ(63, named_access_count);
 
   // Check that the indexed access-control function is called every time.
-  value = v8_compile("for (var i = 0; i < 10; i++) obj[0] = 1;")->Run();
-  value = v8_compile("for (var i = 0; i < 10; i++) obj[0];"
-                     "obj[0]")->Run();
+  CompileRun("function testIndexed(obj) {"
+             "  for (var i = 0; i < 10; i++) obj[0] = 1;"
+             "  for (var j = 0; j < 10; j++) obj[0];"
+             "  return obj[0]"
+             "}");
+  value = CompileRun("testIndexed(obj)");
   CHECK(value->IsNumber());
   CHECK_EQ(1, value->Int32Value());
   CHECK_EQ(21, indexed_access_count);
+  // Force the inline caches into generic state.
+  CompileRun("testIndexed(new Array(1))");
+  // Test that the indexed access check is called.
+  value = CompileRun("testIndexed(obj)");
+  CHECK(value->IsNumber());
+  CHECK_EQ(1, value->Int32Value());
+  CHECK_EQ(42, indexed_access_count);
+
+  // Check that the named access check is called when invoking
+  // functions on an object that requires access checks.
+  CompileRun("obj.f = function() {}");
+  CompileRun("function testCallNormal(obj) {"
+             "  for (var i = 0; i < 10; i++) obj.f();"
+             "}");
+  CompileRun("testCallNormal(obj)");
+  CHECK_EQ(74, named_access_count);
+
+  // Force obj into slow case.
+  value = CompileRun("delete obj.prop");
+  CHECK(value->BooleanValue());
+  // Force inline caches into dictionary probing mode.
+  CompileRun("var o = { x: 0 }; delete o.x; testProp(o);");
+  // Test that the named access check is called.
+  value = CompileRun("testProp(obj);");
+  CHECK(value->IsNumber());
+  CHECK_EQ(1, value->Int32Value());
+  CHECK_EQ(96, named_access_count);
+
+  // Force the call inline cache into dictionary probing mode.
+  CompileRun("o.f = function() {}; testCallNormal(o)");
+  // Test that the named access check is still called for each
+  // invocation of the function.
+  value = CompileRun("testCallNormal(obj)");
+  CHECK_EQ(106, named_access_count);
 
   context1->Exit();
   context0->Exit();