From 62a9497787fd995e2d891809d678deddd0749f6e Mon Sep 17 00:00:00 2001 From: "antonm@chromium.org" Date: Fri, 12 Feb 2010 14:21:18 +0000 Subject: [PATCH] Compile very thin code to access objects on which indexed interceptor is set. Review URL: http://codereview.chromium.org/603028 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3847 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/ic-arm.cc | 5 ++ src/builtins.cc | 4 + src/builtins.h | 1 + src/ia32/ic-ia32.cc | 46 ++++++++++++ src/ic.cc | 2 + src/ic.h | 6 ++ src/stub-cache.cc | 7 ++ src/stub-cache.h | 1 + src/x64/ic-x64.cc | 5 ++ test/cctest/test-api.cc | 189 ++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 266 insertions(+) diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index b48958a..8fce7fd 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -635,6 +635,11 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, } +void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { + GenerateGeneric(masm); +} + + void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { // ---------- S t a t e -------------- // -- r0 : value diff --git a/src/builtins.cc b/src/builtins.cc index f9d5c76..9c4cdac 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -705,6 +705,10 @@ static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) { KeyedLoadIC::GeneratePreMonomorphic(masm); } +static void Generate_KeyedLoadIC_IndexedInterceptor(MacroAssembler* masm) { + KeyedLoadIC::GenerateIndexedInterceptor(masm); +} + static void Generate_StoreIC_Initialize(MacroAssembler* masm) { StoreIC::GenerateInitialize(masm); diff --git a/src/builtins.h b/src/builtins.h index d7b3e2b..9df6ef8 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -89,6 +89,7 @@ enum BuiltinExtraArguments { V(KeyedLoadIC_ExternalIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \ V(KeyedLoadIC_ExternalUnsignedIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \ V(KeyedLoadIC_ExternalFloatArray, KEYED_LOAD_IC, MEGAMORPHIC) \ + V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC) \ \ V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \ V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \ diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 3c62337..6c66361 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -586,6 +586,52 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, } +void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { + // ----------- S t a t e ------------- + // -- eax : value + // -- esp[0] : return address + // -- esp[4] : key + // -- esp[8] : receiver + // ----------------------------------- + Label slow; + + // Load key and receiver. + __ mov(eax, Operand(esp, kPointerSize)); + __ mov(ecx, Operand(esp, 2 * kPointerSize)); + + // Check that the receiver isn't a smi. + __ test(ecx, Immediate(kSmiTagMask)); + __ j(zero, &slow, not_taken); + + // Check that the key is a smi. + __ test(eax, Immediate(kSmiTagMask)); + __ j(not_zero, &slow, not_taken); + + // Get the map of the receiver. + __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset)); + + // Check that it has indexed interceptor and access checks + // are not enabled for this object. + __ movzx_b(edx, FieldOperand(edx, Map::kBitFieldOffset)); + __ and_(Operand(edx), Immediate(kSlowCaseBitFieldMask)); + __ cmp(Operand(edx), Immediate(1 << Map::kHasIndexedInterceptor)); + __ j(not_zero, &slow, not_taken); + + // Everything is fine, call runtime. + __ pop(edx); + __ push(ecx); // receiver + __ push(eax); // key + __ push(edx); // return address + + // Perform tail call to the entry. + __ TailCallRuntime(ExternalReference( + IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1); + + __ bind(&slow); + GenerateMiss(masm); +} + + void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- eax : value diff --git a/src/ic.cc b/src/ic.cc index 9c7e0b5..1ab57da 100644 --- a/src/ic.cc +++ b/src/ic.cc @@ -896,6 +896,8 @@ Object* KeyedLoadIC::Load(State state, Handle receiver = Handle::cast(object); if (receiver->HasExternalArrayElements()) { stub = external_array_stub(receiver->GetElementsKind()); + } else if (receiver->HasIndexedInterceptor()) { + stub = indexed_interceptor_stub(); } } set_target(stub); diff --git a/src/ic.h b/src/ic.h index b34236f..feff8c5 100644 --- a/src/ic.h +++ b/src/ic.h @@ -53,6 +53,7 @@ enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE }; ICU(LoadPropertyWithInterceptorOnly) \ ICU(LoadPropertyWithInterceptorForLoad) \ ICU(LoadPropertyWithInterceptorForCall) \ + ICU(KeyedLoadPropertyWithInterceptor) \ ICU(StoreInterceptorProperty) // @@ -293,6 +294,7 @@ class KeyedLoadIC: public IC { // for all other types. static void GenerateExternalArray(MacroAssembler* masm, ExternalArrayType array_type); + static void GenerateIndexedInterceptor(MacroAssembler* masm); // Clear the use of the inlined version. static void ClearInlinedVersion(Address address); @@ -329,6 +331,10 @@ class KeyedLoadIC: public IC { } static Code* external_array_stub(JSObject::ElementsKind elements_kind); + static Code* indexed_interceptor_stub() { + return Builtins::builtin(Builtins::KeyedLoadIC_IndexedInterceptor); + } + static void Clear(Address address, Code* target); // Support for patching the map that is checked in an inlined diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 81f89fd..16acc74 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -920,6 +920,13 @@ Object* StoreInterceptorProperty(Arguments args) { } +Object* KeyedLoadPropertyWithInterceptor(Arguments args) { + JSObject* receiver = JSObject::cast(args[0]); + uint32_t index = Smi::cast(args[1])->value(); + return receiver->GetElementWithInterceptor(receiver, index); +} + + Object* StubCompiler::CompileCallInitialize(Code::Flags flags) { HandleScope scope; int argc = Code::ExtractArgumentsCountFromFlags(flags); diff --git a/src/stub-cache.h b/src/stub-cache.h index 72cf40f..8acd260 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -312,6 +312,7 @@ Object* LoadPropertyWithInterceptorForLoad(Arguments args); Object* LoadPropertyWithInterceptorForCall(Arguments args); Object* StoreInterceptorProperty(Arguments args); Object* CallInterceptorProperty(Arguments args); +Object* KeyedLoadPropertyWithInterceptor(Arguments args); // Support function for computing call IC miss stubs. diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 7ca42a6..da06aa0 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -573,6 +573,11 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm, } +void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) { + GenerateGeneric(masm); +} + + void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) { // ----------- S t a t e ------------- // -- rax : value diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc index e5e1e0d..4e617be 100644 --- a/test/cctest/test-api.cc +++ b/test/cctest/test-api.cc @@ -2529,6 +2529,195 @@ THREADED_TEST(IndexedInterceptorWithNoSetter) { } +THREADED_TEST(IndexedInterceptorWithAccessorCheck) { + v8::HandleScope scope; + Local templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local obj = templ->NewInstance(); + obj->TurnOnAccessCheck(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var v = obj[0];" + " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) { + i::FLAG_allow_natives_syntax = true; + v8::HandleScope scope; + Local templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 5) {" + " %EnableAccessChecks(obj);" + " expected = undefined;" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " if (i == 5) %DisableAccessChecks(obj);" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorWithDifferentIndices) { + v8::HandleScope scope; + Local templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var v = obj[i];" + " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorWithNotSmiLookup) { + v8::HandleScope scope; + Local templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 50) {" + " i = 'foobar';" + " expected = undefined;" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorGoingMegamorphic) { + v8::HandleScope scope; + Local templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "var original = obj;" + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 50) {" + " obj = {50: 'foobar'};" + " expected = 'foobar';" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " if (i == 50) obj = original;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorReceiverTurningSmi) { + v8::HandleScope scope; + Local templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "var original = obj;" + "try {" + " for (var i = 0; i < 100; i++) {" + " var expected = i;" + " if (i == 5) {" + " obj = 239;" + " expected = undefined;" + " }" + " var v = obj[i];" + " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;" + " if (i == 5) obj = original;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + +THREADED_TEST(IndexedInterceptorOnProto) { + v8::HandleScope scope; + Local templ = ObjectTemplate::New(); + templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter); + + LocalContext context; + Local obj = templ->NewInstance(); + context->Global()->Set(v8_str("obj"), obj); + + const char* code = + "var o = {__proto__: obj};" + "try {" + " for (var i = 0; i < 100; i++) {" + " var v = o[i];" + " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;" + " }" + " 'PASSED'" + "} catch(e) {" + " e" + "}"; + ExpectString(code, "PASSED"); +} + + THREADED_TEST(MultiContexts) { v8::HandleScope scope; v8::Handle templ = ObjectTemplate::New(); -- 2.7.4