Index: src/ic.cc |
diff --git a/src/ic.cc b/src/ic.cc |
index 1a8366dfeca114a74b8c2c361252908a2062922f..0dc62aa93d428629878041bedc6d903757034412 100644 |
--- a/src/ic.cc |
+++ b/src/ic.cc |
@@ -190,48 +190,32 @@ Code* IC::GetOriginalCode() const { |
} |
-static bool HasInterceptorGetter(JSObject* object) { |
- return !object->GetNamedInterceptor()->getter()->IsUndefined(); |
-} |
- |
- |
static bool HasInterceptorSetter(JSObject* object) { |
return !object->GetNamedInterceptor()->setter()->IsUndefined(); |
} |
-static void LookupForRead(Handle<Object> object, |
- Handle<String> name, |
- LookupResult* lookup) { |
- // Skip all the objects with named interceptors, but |
- // without actual getter. |
- while (true) { |
- object->Lookup(name, lookup); |
- // Besides normal conditions (property not found or it's not |
- // an interceptor), bail out if lookup is not cacheable: we won't |
- // be able to IC it anyway and regular lookup should work fine. |
- if (!lookup->IsInterceptor() || !lookup->IsCacheable()) { |
- return; |
- } |
- |
- Handle<JSObject> holder(lookup->holder(), lookup->isolate()); |
- if (HasInterceptorGetter(*holder)) { |
- return; |
- } |
- |
- holder->LookupOwnRealNamedProperty(name, lookup); |
- if (lookup->IsFound()) { |
- ASSERT(!lookup->IsInterceptor()); |
- return; |
- } |
- |
- PrototypeIterator iter(lookup->isolate(), holder); |
- if (iter.IsAtEnd()) { |
- ASSERT(!lookup->IsFound()); |
- return; |
+static void LookupForRead(LookupIterator* it) { |
+ for (; it->IsFound(); it->Next()) { |
+ switch (it->state()) { |
+ case LookupIterator::NOT_FOUND: |
+ UNREACHABLE(); |
+ case LookupIterator::JSPROXY: |
+ return; |
+ case LookupIterator::INTERCEPTOR: { |
+ // If there is a getter, return; otherwise loop to perform the lookup. |
+ Handle<JSObject> holder = it->GetHolder<JSObject>(); |
+ if (!holder->GetNamedInterceptor()->getter()->IsUndefined()) { |
+ return; |
+ } |
+ break; |
+ } |
+ case LookupIterator::ACCESS_CHECK: |
+ return; |
+ case LookupIterator::PROPERTY: |
+ if (it->HasProperty()) return; // Yay! |
+ break; |
} |
- |
- object = PrototypeIterator::GetCurrent(iter); |
} |
} |
@@ -574,11 +558,11 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<String> name) { |
bool use_ic = MigrateDeprecated(object) ? false : FLAG_use_ic; |
// Named lookup in the object. |
- LookupResult lookup(isolate()); |
- LookupForRead(object, name, &lookup); |
+ LookupIterator it(object, name); |
+ LookupForRead(&it); |
// If we did not find a property, check if we need to throw an exception. |
- if (!lookup.IsFound()) { |
+ if (!it.IsFound()) { |
if (IsUndeclaredGlobal(object)) { |
return ReferenceError("not_defined", name); |
} |
@@ -586,16 +570,14 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<String> name) { |
} |
// Update inline cache and stub cache. |
- if (use_ic) UpdateCaches(&lookup, object, name); |
+ if (use_ic) UpdateCaches(&it, object, name); |
// Get the property. |
- LookupIterator it(object, name); |
Handle<Object> result; |
ASSIGN_RETURN_ON_EXCEPTION( |
isolate(), result, Object::GetProperty(&it), Object); |
// If the property is not present, check if we need to throw an exception. |
- if ((lookup.IsInterceptor() || lookup.IsHandler()) && |
- !it.IsFound() && IsUndeclaredGlobal(object)) { |
+ if (!it.IsFound() && IsUndeclaredGlobal(object)) { |
return ReferenceError("not_defined", name); |
} |
@@ -828,8 +810,7 @@ Handle<Code> LoadIC::SimpleFieldLoad(FieldIndex index) { |
} |
-void LoadIC::UpdateCaches(LookupResult* lookup, |
- Handle<Object> object, |
+void LoadIC::UpdateCaches(LookupIterator* lookup, Handle<Object> object, |
Handle<String> name) { |
if (state() == UNINITIALIZED) { |
// This is the first time we execute this inline cache. |
@@ -841,10 +822,10 @@ void LoadIC::UpdateCaches(LookupResult* lookup, |
} |
Handle<Code> code; |
- if (!lookup->IsCacheable()) { |
- // Bail out if the result is not cacheable. |
+ if (lookup->state() == LookupIterator::JSPROXY || |
+ lookup->state() == LookupIterator::ACCESS_CHECK) { |
code = slow_stub(); |
- } else if (!lookup->IsProperty()) { |
+ } else if (!lookup->IsFound()) { |
if (kind() == Code::LOAD_IC) { |
code = NamedLoadHandlerCompiler::ComputeLoadNonexistent(name, |
receiver_type()); |
@@ -869,10 +850,53 @@ void IC::UpdateMegamorphicCache(HeapType* type, Name* name, Code* code) { |
} |
-Handle<Code> IC::ComputeHandler(LookupResult* lookup, |
- Handle<Object> object, |
- Handle<String> name, |
- Handle<Object> value) { |
+Handle<Code> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> object, |
+ Handle<String> name, Handle<Object> value) { |
+ bool receiver_is_holder = |
+ object.is_identical_to(lookup->GetHolder<JSObject>()); |
+ CacheHolderFlag flag; |
+ Handle<Map> stub_holder_map = IC::GetHandlerCacheHolder( |
+ *receiver_type(), receiver_is_holder, isolate(), &flag); |
+ |
+ Handle<Code> code = PropertyHandlerCompiler::Find( |
+ name, stub_holder_map, kind(), flag, |
+ lookup->holder_map()->is_dictionary_map() ? Code::NORMAL : Code::FAST); |
+ // Use the cached value if it exists, and if it is different from the |
+ // handler that just missed. |
+ if (!code.is_null()) { |
+ if (!maybe_handler_.is_null() && |
+ !maybe_handler_.ToHandleChecked().is_identical_to(code)) { |
+ return code; |
+ } |
+ if (maybe_handler_.is_null()) { |
+ // maybe_handler_ is only populated for MONOMORPHIC and POLYMORPHIC ICs. |
+ // In MEGAMORPHIC case, check if the handler in the megamorphic stub |
+ // cache (which just missed) is different from the cached handler. |
+ if (state() == MEGAMORPHIC && object->IsHeapObject()) { |
+ Map* map = Handle<HeapObject>::cast(object)->map(); |
+ Code* megamorphic_cached_code = |
+ isolate()->stub_cache()->Get(*name, map, code->flags()); |
+ if (megamorphic_cached_code != *code) return code; |
+ } else { |
+ return code; |
+ } |
+ } |
+ } |
+ |
+ code = CompileHandler(lookup, object, name, value, flag); |
+ ASSERT(code->is_handler()); |
+ |
+ if (code->type() != Code::NORMAL) { |
+ Map::UpdateCodeCache(stub_holder_map, name, code); |
+ } |
+ |
+ return code; |
+} |
+ |
+ |
+Handle<Code> IC::ComputeStoreHandler(LookupResult* lookup, |
+ Handle<Object> object, Handle<String> name, |
+ Handle<Object> value) { |
bool receiver_is_holder = lookup->ReceiverIsHolder(object); |
CacheHolderFlag flag; |
Handle<Map> stub_holder_map = IC::GetHandlerCacheHolder( |
@@ -903,7 +927,7 @@ Handle<Code> IC::ComputeHandler(LookupResult* lookup, |
} |
} |
- code = CompileHandler(lookup, object, name, value, flag); |
+ code = CompileStoreHandler(lookup, object, name, value, flag); |
ASSERT(code->is_handler()); |
if (code->type() != Code::NORMAL) { |
@@ -914,8 +938,9 @@ Handle<Code> IC::ComputeHandler(LookupResult* lookup, |
} |
-Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, Handle<Object> object, |
- Handle<String> name, Handle<Object> unused, |
+Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup, |
+ Handle<Object> object, Handle<String> name, |
+ Handle<Object> unused, |
CacheHolderFlag cache_holder) { |
if (object->IsString() && |
String::Equals(isolate()->factory()->length_string(), name)) { |
@@ -940,102 +965,107 @@ Handle<Code> LoadIC::CompileHandler(LookupResult* lookup, Handle<Object> object, |
} |
Handle<HeapType> type = receiver_type(); |
- Handle<JSObject> holder(lookup->holder()); |
+ Handle<JSObject> holder = lookup->GetHolder<JSObject>(); |
bool receiver_is_holder = object.is_identical_to(holder); |
NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder, |
cache_holder); |
- switch (lookup->type()) { |
- case FIELD: { |
- FieldIndex field = lookup->GetFieldIndex(); |
- if (receiver_is_holder) { |
- return SimpleFieldLoad(field); |
+ // -------------- Interceptors -------------- |
+ if (lookup->state() == LookupIterator::INTERCEPTOR) { |
+ ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); |
+ return compiler.CompileLoadInterceptor(name); |
+ } |
+ ASSERT(lookup->state() == LookupIterator::PROPERTY); |
+ |
+ // -------------- Accessors -------------- |
+ if (lookup->property_kind() == LookupIterator::ACCESSOR) { |
+ // Use simple field loads for some well-known callback properties. |
+ if (receiver_is_holder) { |
+ ASSERT(object->IsJSObject()); |
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object); |
+ int object_offset; |
+ if (Accessors::IsJSObjectFieldAccessor<HeapType>(type, name, |
+ &object_offset)) { |
+ FieldIndex index = |
+ FieldIndex::ForInObjectOffset(object_offset, receiver->map()); |
+ return SimpleFieldLoad(index); |
} |
- return compiler.CompileLoadField(name, field, lookup->representation()); |
} |
- case CONSTANT: { |
- Handle<Object> constant(lookup->GetConstant(), isolate()); |
- return compiler.CompileLoadConstant(name, constant); |
+ |
+ Handle<Object> accessors = lookup->GetAccessors(); |
+ if (accessors->IsExecutableAccessorInfo()) { |
+ Handle<ExecutableAccessorInfo> info = |
+ Handle<ExecutableAccessorInfo>::cast(accessors); |
+ if (v8::ToCData<Address>(info->getter()) == 0) return slow_stub(); |
+ if (!ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), info, |
+ type)) { |
+ return slow_stub(); |
+ } |
+ if (holder->IsGlobalObject()) return slow_stub(); |
+ return compiler.CompileLoadCallback(name, info); |
} |
- case NORMAL: |
- if (kind() != Code::LOAD_IC) break; |
- if (holder->IsGlobalObject()) { |
- Handle<GlobalObject> global = Handle<GlobalObject>::cast(holder); |
- Handle<PropertyCell> cell( |
- global->GetPropertyCell(lookup), isolate()); |
- Handle<Code> code = |
- compiler.CompileLoadGlobal(cell, name, lookup->IsDontDelete()); |
- // TODO(verwaest): Move caching of these NORMAL stubs outside as well. |
- CacheHolderFlag flag; |
- Handle<Map> stub_holder_map = |
- GetHandlerCacheHolder(*type, receiver_is_holder, isolate(), &flag); |
- Map::UpdateCodeCache(stub_holder_map, name, code); |
- return code; |
+ if (accessors->IsAccessorPair()) { |
+ Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(), |
+ isolate()); |
+ if (!getter->IsJSFunction()) return slow_stub(); |
+ if (holder->IsGlobalObject()) return slow_stub(); |
+ if (!holder->HasFastProperties()) return slow_stub(); |
+ Handle<JSFunction> function = Handle<JSFunction>::cast(getter); |
+ if (!object->IsJSObject() && !function->IsBuiltin() && |
+ function->shared()->strict_mode() == SLOPPY) { |
+ // Calling sloppy non-builtins with a value as the receiver |
+ // requires boxing. |
+ return slow_stub(); |
} |
- // There is only one shared stub for loading normalized |
- // properties. It does not traverse the prototype chain, so the |
- // property must be found in the object for the stub to be |
- // applicable. |
- if (!receiver_is_holder) break; |
- return isolate()->builtins()->LoadIC_Normal(); |
- case CALLBACKS: { |
- // Use simple field loads for some well-known callback properties. |
- if (receiver_is_holder) { |
- ASSERT(object->IsJSObject()); |
- Handle<JSObject> receiver = Handle<JSObject>::cast(object); |
- int object_offset; |
- if (Accessors::IsJSObjectFieldAccessor<HeapType>( |
- type, name, &object_offset)) { |
- FieldIndex index = FieldIndex::ForInObjectOffset( |
- object_offset, receiver->map()); |
- return SimpleFieldLoad(index); |
- } |
+ CallOptimization call_optimization(function); |
+ if (call_optimization.is_simple_api_call() && |
+ call_optimization.IsCompatibleReceiver(object, holder)) { |
+ return compiler.CompileLoadCallback(name, call_optimization); |
} |
- |
- Handle<Object> callback(lookup->GetCallbackObject(), isolate()); |
- if (callback->IsExecutableAccessorInfo()) { |
- Handle<ExecutableAccessorInfo> info = |
- Handle<ExecutableAccessorInfo>::cast(callback); |
- if (v8::ToCData<Address>(info->getter()) == 0) break; |
- if (!ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), info, |
- type)) { |
- break; |
- } |
- if (holder->IsGlobalObject()) break; |
- return compiler.CompileLoadCallback(name, info); |
- } else if (callback->IsAccessorPair()) { |
- Handle<Object> getter(Handle<AccessorPair>::cast(callback)->getter(), |
- isolate()); |
- if (!getter->IsJSFunction()) break; |
- if (holder->IsGlobalObject()) break; |
- if (!holder->HasFastProperties()) break; |
- Handle<JSFunction> function = Handle<JSFunction>::cast(getter); |
- if (!object->IsJSObject() && |
- !function->IsBuiltin() && |
- function->shared()->strict_mode() == SLOPPY) { |
- // Calling sloppy non-builtins with a value as the receiver |
- // requires boxing. |
- break; |
- } |
- CallOptimization call_optimization(function); |
- if (call_optimization.is_simple_api_call() && |
- call_optimization.IsCompatibleReceiver(object, holder)) { |
- return compiler.CompileLoadCallback(name, call_optimization); |
- } |
- return compiler.CompileLoadViaGetter(name, function); |
- } |
- // TODO(dcarney): Handle correctly. |
- ASSERT(callback->IsDeclaredAccessorInfo()); |
- break; |
+ return compiler.CompileLoadViaGetter(name, function); |
} |
- case INTERCEPTOR: |
- ASSERT(HasInterceptorGetter(*holder)); |
- return compiler.CompileLoadInterceptor(name); |
- default: |
- break; |
+ // TODO(dcarney): Handle correctly. |
+ ASSERT(accessors->IsDeclaredAccessorInfo()); |
+ return slow_stub(); |
+ } |
+ |
+ // -------------- Dictionary properties -------------- |
+ ASSERT(lookup->property_kind() == LookupIterator::DATA); |
+ if (lookup->property_encoding() == LookupIterator::DICTIONARY) { |
+ if (kind() != Code::LOAD_IC) return slow_stub(); |
+ if (holder->IsGlobalObject()) { |
+ Handle<PropertyCell> cell = lookup->GetPropertyCell(); |
+ Handle<Code> code = |
+ compiler.CompileLoadGlobal(cell, name, lookup->IsConfigurable()); |
+ // TODO(verwaest): Move caching of these NORMAL stubs outside as well. |
+ CacheHolderFlag flag; |
+ Handle<Map> stub_holder_map = |
+ GetHandlerCacheHolder(*type, receiver_is_holder, isolate(), &flag); |
+ Map::UpdateCodeCache(stub_holder_map, name, code); |
+ return code; |
+ } |
+ // There is only one shared stub for loading normalized |
+ // properties. It does not traverse the prototype chain, so the |
+ // property must be found in the object for the stub to be |
+ // applicable. |
+ if (!receiver_is_holder) return slow_stub(); |
+ return isolate()->builtins()->LoadIC_Normal(); |
+ } |
+ |
+ // -------------- Fields -------------- |
+ ASSERT(lookup->property_encoding() == LookupIterator::DESCRIPTOR); |
+ if (lookup->property_details().type() == FIELD) { |
+ FieldIndex field = lookup->GetFieldIndex(); |
+ if (receiver_is_holder) { |
+ return SimpleFieldLoad(field); |
+ } |
+ return compiler.CompileLoadField(name, field, lookup->representation()); |
} |
- return slow_stub(); |
+ // -------------- Constant properties -------------- |
+ ASSERT(lookup->property_details().type() == CONSTANT); |
+ Handle<Object> constant = lookup->GetDataValue(); |
+ return compiler.CompileLoadConstant(name, constant); |
} |
@@ -1377,17 +1407,18 @@ void StoreIC::UpdateCaches(LookupResult* lookup, |
// These are not cacheable, so we never see such LookupResults here. |
ASSERT(!lookup->IsHandler()); |
- Handle<Code> code = ComputeHandler(lookup, receiver, name, value); |
+ Handle<Code> code = ComputeStoreHandler(lookup, receiver, name, value); |
PatchCache(name, code); |
TRACE_IC("StoreIC", name); |
} |
-Handle<Code> StoreIC::CompileHandler(LookupResult* lookup, |
- Handle<Object> object, Handle<String> name, |
- Handle<Object> value, |
- CacheHolderFlag cache_holder) { |
+Handle<Code> StoreIC::CompileStoreHandler(LookupResult* lookup, |
+ Handle<Object> object, |
+ Handle<String> name, |
+ Handle<Object> value, |
+ CacheHolderFlag cache_holder) { |
if (object->IsAccessCheckNeeded()) return slow_stub(); |
ASSERT(cache_holder == kCacheOnReceiver || lookup->type() == CALLBACKS || |
(object->IsJSGlobalProxy() && lookup->holder()->IsJSGlobalObject())); |