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
This commit is contained in:
parent
f937e7daa5
commit
62a9497787
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -896,6 +896,8 @@ Object* KeyedLoadIC::Load(State state,
|
||||
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
||||
if (receiver->HasExternalArrayElements()) {
|
||||
stub = external_array_stub(receiver->GetElementsKind());
|
||||
} else if (receiver->HasIndexedInterceptor()) {
|
||||
stub = indexed_interceptor_stub();
|
||||
}
|
||||
}
|
||||
set_target(stub);
|
||||
|
6
src/ic.h
6
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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -2529,6 +2529,195 @@ THREADED_TEST(IndexedInterceptorWithNoSetter) {
|
||||
}
|
||||
|
||||
|
||||
THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
|
||||
v8::HandleScope scope;
|
||||
Local<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
|
||||
|
||||
LocalContext context;
|
||||
Local<v8::Object> 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<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
|
||||
|
||||
LocalContext context;
|
||||
Local<v8::Object> 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<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
|
||||
|
||||
LocalContext context;
|
||||
Local<v8::Object> 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<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
|
||||
|
||||
LocalContext context;
|
||||
Local<v8::Object> 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<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
|
||||
|
||||
LocalContext context;
|
||||
Local<v8::Object> 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<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
|
||||
|
||||
LocalContext context;
|
||||
Local<v8::Object> 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<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
|
||||
|
||||
LocalContext context;
|
||||
Local<v8::Object> 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<ObjectTemplate> templ = ObjectTemplate::New();
|
||||
|
Loading…
Reference in New Issue
Block a user