| Index: src/ic.cc
|
| diff --git a/src/ic.cc b/src/ic.cc
|
| index ceed585d056b9dae7501e7b121b9292037171323..4d0b2471556b7b2d3f970b4ca66e5767016df3ee 100644
|
| --- a/src/ic.cc
|
| +++ b/src/ic.cc
|
| @@ -202,15 +202,11 @@ Code* IC::GetOriginalCode() const {
|
| }
|
|
|
|
|
| -static bool HasInterceptorSetter(JSObject* object) {
|
| - return !object->GetNamedInterceptor()->setter()->IsUndefined();
|
| -}
|
| -
|
| -
|
| static void LookupForRead(LookupIterator* it) {
|
| for (; it->IsFound(); it->Next()) {
|
| switch (it->state()) {
|
| case LookupIterator::NOT_FOUND:
|
| + case LookupIterator::TRANSITION:
|
| UNREACHABLE();
|
| case LookupIterator::JSPROXY:
|
| return;
|
| @@ -302,7 +298,7 @@ bool IC::IsNameCompatibleWithPrototypeFailure(Handle<Object> name) {
|
|
|
|
|
| void IC::UpdateState(Handle<Object> receiver, Handle<Object> name) {
|
| - receiver_type_ = CurrentTypeOf(receiver, isolate());
|
| + update_receiver_type(receiver);
|
| if (!name->IsString()) return;
|
| if (state() != MONOMORPHIC && state() != POLYMORPHIC) return;
|
| if (receiver->IsUndefined() || receiver->IsNull()) return;
|
| @@ -621,27 +617,22 @@ MaybeHandle<Object> LoadIC::Load(Handle<Object> object, Handle<Name> name) {
|
| LookupIterator it(object, name);
|
| LookupForRead(&it);
|
|
|
| - // If we did not find a property, check if we need to throw an exception.
|
| - if (!it.IsFound()) {
|
| - if (IsUndeclaredGlobal(object)) {
|
| - return ReferenceError("not_defined", name);
|
| - }
|
| - LOG(isolate(), SuspectReadEvent(*name, *object));
|
| - }
|
| + if (it.IsFound() || !IsUndeclaredGlobal(object)) {
|
| + // Update inline cache and stub cache.
|
| + if (use_ic) UpdateCaches(&it);
|
|
|
| - // Update inline cache and stub cache.
|
| - if (use_ic) UpdateCaches(&it, object, name);
|
| -
|
| - // Get the property.
|
| - 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 (!it.IsFound() && IsUndeclaredGlobal(object)) {
|
| - return ReferenceError("not_defined", name);
|
| + // Get the property.
|
| + Handle<Object> result;
|
| + ASSIGN_RETURN_ON_EXCEPTION(isolate(), result, Object::GetProperty(&it),
|
| + Object);
|
| + if (it.IsFound()) {
|
| + return result;
|
| + } else if (!IsUndeclaredGlobal(object)) {
|
| + LOG(isolate(), SuspectReadEvent(*name, *object));
|
| + return result;
|
| + }
|
| }
|
| -
|
| - return result;
|
| + return ReferenceError("not_defined", name);
|
| }
|
|
|
|
|
| @@ -871,14 +862,12 @@ Handle<Code> LoadIC::SimpleFieldLoad(FieldIndex index) {
|
| }
|
|
|
|
|
| -void LoadIC::UpdateCaches(LookupIterator* lookup, Handle<Object> object,
|
| - Handle<Name> name) {
|
| +void LoadIC::UpdateCaches(LookupIterator* lookup) {
|
| if (state() == UNINITIALIZED) {
|
| - // This is the first time we execute this inline cache.
|
| - // Set the target to the pre monomorphic stub to delay
|
| - // setting the monomorphic state.
|
| + // This is the first time we execute this inline cache. Set the target to
|
| + // the pre monomorphic stub to delay setting the monomorphic state.
|
| set_target(*pre_monomorphic_stub());
|
| - TRACE_IC("LoadIC", name);
|
| + TRACE_IC("LoadIC", lookup->name());
|
| return;
|
| }
|
|
|
| @@ -888,7 +877,7 @@ void LoadIC::UpdateCaches(LookupIterator* lookup, Handle<Object> object,
|
| code = slow_stub();
|
| } else if (!lookup->IsFound()) {
|
| if (kind() == Code::LOAD_IC) {
|
| - code = NamedLoadHandlerCompiler::ComputeLoadNonexistent(name,
|
| + code = NamedLoadHandlerCompiler::ComputeLoadNonexistent(lookup->name(),
|
| receiver_type());
|
| // TODO(jkummerow/verwaest): Introduce a builtin that handles this case.
|
| if (code.is_null()) code = slow_stub();
|
| @@ -896,11 +885,11 @@ void LoadIC::UpdateCaches(LookupIterator* lookup, Handle<Object> object,
|
| code = slow_stub();
|
| }
|
| } else {
|
| - code = ComputeHandler(lookup, object, name);
|
| + code = ComputeHandler(lookup);
|
| }
|
|
|
| - PatchCache(name, code);
|
| - TRACE_IC("LoadIC", name);
|
| + PatchCache(lookup->name(), code);
|
| + TRACE_IC("LoadIC", lookup->name());
|
| }
|
|
|
|
|
| @@ -911,16 +900,15 @@ void IC::UpdateMegamorphicCache(HeapType* type, Name* name, Code* code) {
|
| }
|
|
|
|
|
| -Handle<Code> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> object,
|
| - Handle<Name> name, Handle<Object> value) {
|
| +Handle<Code> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> value) {
|
| bool receiver_is_holder =
|
| - object.is_identical_to(lookup->GetHolder<JSObject>());
|
| + lookup->GetReceiver().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->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.
|
| @@ -933,54 +921,10 @@ Handle<Code> IC::ComputeHandler(LookupIterator* lookup, Handle<Object> object,
|
| // 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);
|
| - DCHECK(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<Name> name,
|
| - Handle<Object> value) {
|
| - bool receiver_is_holder = lookup->ReceiverIsHolder(object);
|
| - 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, handler_kind(), flag,
|
| - lookup->holder()->HasFastProperties() ? Code::FAST : Code::NORMAL);
|
| - // 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();
|
| + if (state() == MEGAMORPHIC && lookup->GetReceiver()->IsHeapObject()) {
|
| + Map* map = Handle<HeapObject>::cast(lookup->GetReceiver())->map();
|
| Code* megamorphic_cached_code =
|
| - isolate()->stub_cache()->Get(*name, map, code->flags());
|
| + isolate()->stub_cache()->Get(*lookup->name(), map, code->flags());
|
| if (megamorphic_cached_code != *code) return code;
|
| } else {
|
| return code;
|
| @@ -988,11 +932,11 @@ Handle<Code> IC::ComputeStoreHandler(LookupResult* lookup,
|
| }
|
| }
|
|
|
| - code = CompileStoreHandler(lookup, object, name, value, flag);
|
| + code = CompileHandler(lookup, value, flag);
|
| DCHECK(code->is_handler());
|
|
|
| if (code->type() != Code::NORMAL) {
|
| - Map::UpdateCodeCache(stub_holder_map, name, code);
|
| + Map::UpdateCodeCache(stub_holder_map, lookup->name(), code);
|
| }
|
|
|
| return code;
|
| @@ -1000,26 +944,28 @@ Handle<Code> IC::ComputeStoreHandler(LookupResult* lookup,
|
|
|
|
|
| Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| - Handle<Object> object, Handle<Name> name,
|
| Handle<Object> unused,
|
| CacheHolderFlag cache_holder) {
|
| - if (object->IsString() &&
|
| - Name::Equals(isolate()->factory()->length_string(), name)) {
|
| + Handle<Object> receiver = lookup->GetReceiver();
|
| + if (receiver->IsString() &&
|
| + Name::Equals(isolate()->factory()->length_string(), lookup->name())) {
|
| FieldIndex index = FieldIndex::ForInObjectOffset(String::kLengthOffset);
|
| return SimpleFieldLoad(index);
|
| }
|
|
|
| - if (object->IsStringWrapper() &&
|
| - Name::Equals(isolate()->factory()->length_string(), name)) {
|
| + if (receiver->IsStringWrapper() &&
|
| + Name::Equals(isolate()->factory()->length_string(), lookup->name())) {
|
| StringLengthStub string_length_stub(isolate());
|
| return string_length_stub.GetCode();
|
| }
|
|
|
| // Use specialized code for getting prototype of functions.
|
| - if (object->IsJSFunction() &&
|
| - Name::Equals(isolate()->factory()->prototype_string(), name) &&
|
| - Handle<JSFunction>::cast(object)->should_have_prototype() &&
|
| - !Handle<JSFunction>::cast(object)->map()->has_non_instance_prototype()) {
|
| + if (receiver->IsJSFunction() &&
|
| + Name::Equals(isolate()->factory()->prototype_string(), lookup->name()) &&
|
| + Handle<JSFunction>::cast(receiver)->should_have_prototype() &&
|
| + !Handle<JSFunction>::cast(receiver)
|
| + ->map()
|
| + ->has_non_instance_prototype()) {
|
| Handle<Code> stub;
|
| FunctionPrototypeStub function_prototype_stub(isolate());
|
| return function_prototype_stub.GetCode();
|
| @@ -1027,7 +973,7 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
|
|
| Handle<HeapType> type = receiver_type();
|
| Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
| - bool receiver_is_holder = object.is_identical_to(holder);
|
| + bool receiver_is_holder = receiver.is_identical_to(holder);
|
| // -------------- Interceptors --------------
|
| if (lookup->state() == LookupIterator::INTERCEPTOR) {
|
| DCHECK(!holder->GetNamedInterceptor()->getter()->IsUndefined());
|
| @@ -1038,21 +984,21 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| LookupIterator it(lookup);
|
| it.Next();
|
| LookupForRead(&it);
|
| - return compiler.CompileLoadInterceptor(&it, name);
|
| + return compiler.CompileLoadInterceptor(&it);
|
| }
|
| - DCHECK(lookup->state() == LookupIterator::PROPERTY);
|
|
|
| // -------------- Accessors --------------
|
| + DCHECK(lookup->state() == LookupIterator::PROPERTY);
|
| if (lookup->property_kind() == LookupIterator::ACCESSOR) {
|
| // Use simple field loads for some well-known callback properties.
|
| if (receiver_is_holder) {
|
| - DCHECK(object->IsJSObject());
|
| - Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
| + DCHECK(receiver->IsJSObject());
|
| + Handle<JSObject> js_receiver = Handle<JSObject>::cast(receiver);
|
| int object_offset;
|
| - if (Accessors::IsJSObjectFieldAccessor<HeapType>(type, name,
|
| + if (Accessors::IsJSObjectFieldAccessor<HeapType>(type, lookup->name(),
|
| &object_offset)) {
|
| FieldIndex index =
|
| - FieldIndex::ForInObjectOffset(object_offset, receiver->map());
|
| + FieldIndex::ForInObjectOffset(object_offset, js_receiver->map());
|
| return SimpleFieldLoad(index);
|
| }
|
| }
|
| @@ -1069,7 +1015,7 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| if (!holder->HasFastProperties()) return slow_stub();
|
| NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
|
| cache_holder);
|
| - return compiler.CompileLoadCallback(name, info);
|
| + return compiler.CompileLoadCallback(lookup->name(), info);
|
| }
|
| if (accessors->IsAccessorPair()) {
|
| Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(),
|
| @@ -1077,7 +1023,7 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| if (!getter->IsJSFunction()) return slow_stub();
|
| if (!holder->HasFastProperties()) return slow_stub();
|
| Handle<JSFunction> function = Handle<JSFunction>::cast(getter);
|
| - if (!object->IsJSObject() && !function->IsBuiltin() &&
|
| + if (!receiver->IsJSObject() && !function->IsBuiltin() &&
|
| function->shared()->strict_mode() == SLOPPY) {
|
| // Calling sloppy non-builtins with a value as the receiver
|
| // requires boxing.
|
| @@ -1087,10 +1033,10 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
|
| cache_holder);
|
| if (call_optimization.is_simple_api_call() &&
|
| - call_optimization.IsCompatibleReceiver(object, holder)) {
|
| - return compiler.CompileLoadCallback(name, call_optimization);
|
| + call_optimization.IsCompatibleReceiver(receiver, holder)) {
|
| + return compiler.CompileLoadCallback(lookup->name(), call_optimization);
|
| }
|
| - return compiler.CompileLoadViaGetter(name, function);
|
| + return compiler.CompileLoadViaGetter(lookup->name(), function);
|
| }
|
| // TODO(dcarney): Handle correctly.
|
| DCHECK(accessors->IsDeclaredAccessorInfo());
|
| @@ -1105,13 +1051,13 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
|
| cache_holder);
|
| Handle<PropertyCell> cell = lookup->GetPropertyCell();
|
| - Handle<Code> code =
|
| - compiler.CompileLoadGlobal(cell, name, lookup->IsConfigurable());
|
| + Handle<Code> code = compiler.CompileLoadGlobal(cell, lookup->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);
|
| + Map::UpdateCodeCache(stub_holder_map, lookup->name(), code);
|
| return code;
|
| }
|
| // There is only one shared stub for loading normalized
|
| @@ -1131,7 +1077,7 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| }
|
| NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
|
| cache_holder);
|
| - return compiler.CompileLoadField(name, field);
|
| + return compiler.CompileLoadField(lookup->name(), field);
|
| }
|
|
|
| // -------------- Constant properties --------------
|
| @@ -1142,7 +1088,8 @@ Handle<Code> LoadIC::CompileHandler(LookupIterator* lookup,
|
| }
|
| NamedLoadHandlerCompiler compiler(isolate(), receiver_type(), holder,
|
| cache_holder);
|
| - return compiler.CompileLoadConstant(name, lookup->GetConstantIndex());
|
| + return compiler.CompileLoadConstant(lookup->name(),
|
| + lookup->GetConstantIndex());
|
| }
|
|
|
|
|
| @@ -1284,70 +1231,60 @@ MaybeHandle<Object> KeyedLoadIC::Load(Handle<Object> object,
|
| }
|
|
|
|
|
| -static bool LookupForWrite(Handle<Object> object, Handle<Name> name,
|
| - Handle<Object> value, LookupResult* lookup, IC* ic) {
|
| +bool StoreIC::LookupForWrite(LookupIterator* it, Handle<Object> value,
|
| + JSReceiver::StoreFromKeyed store_mode) {
|
| // Disable ICs for non-JSObjects for now.
|
| - if (!object->IsJSObject()) return false;
|
| - Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
| + Handle<Object> receiver = it->GetReceiver();
|
| + if (!receiver->IsJSObject()) return false;
|
| + DCHECK(!Handle<JSObject>::cast(receiver)->map()->is_deprecated());
|
|
|
| - Handle<JSObject> holder = receiver;
|
| - receiver->Lookup(name, lookup);
|
| - if (lookup->IsFound()) {
|
| - if (lookup->IsInterceptor() && !HasInterceptorSetter(lookup->holder())) {
|
| - receiver->LookupOwnRealNamedProperty(name, lookup);
|
| - if (!lookup->IsFound()) return false;
|
| - }
|
| + for (; it->IsFound(); it->Next()) {
|
| + switch (it->state()) {
|
| + case LookupIterator::NOT_FOUND:
|
| + case LookupIterator::TRANSITION:
|
| + UNREACHABLE();
|
| + case LookupIterator::JSPROXY:
|
| + return false;
|
| + case LookupIterator::INTERCEPTOR: {
|
| + Handle<JSObject> holder = it->GetHolder<JSObject>();
|
| + InterceptorInfo* info = holder->GetNamedInterceptor();
|
| + if (it->HolderIsReceiverOrHiddenPrototype()) {
|
| + if (!info->setter()->IsUndefined()) return true;
|
| + } else if (!info->getter()->IsUndefined() ||
|
| + !info->query()->IsUndefined()) {
|
| + return false;
|
| + }
|
| + break;
|
| + }
|
| + case LookupIterator::ACCESS_CHECK:
|
| + if (it->GetHolder<JSObject>()->IsAccessCheckNeeded()) return false;
|
| + break;
|
| + case LookupIterator::PROPERTY:
|
| + if (!it->HasProperty()) break;
|
| + if (it->IsReadOnly()) return false;
|
| + if (it->property_kind() == LookupIterator::ACCESSOR) return true;
|
| + if (it->GetHolder<Object>().is_identical_to(receiver)) {
|
| + it->PrepareForDataProperty(value);
|
| + // The previous receiver map might just have been deprecated,
|
| + // so reload it.
|
| + update_receiver_type(receiver);
|
| + return true;
|
| + }
|
| +
|
| + // Receiver != holder.
|
| + if (receiver->IsJSGlobalProxy()) {
|
| + PrototypeIterator iter(it->isolate(), receiver);
|
| + return it->GetHolder<Object>().is_identical_to(
|
| + PrototypeIterator::GetCurrent(iter));
|
| + }
|
|
|
| - if (lookup->IsReadOnly() || !lookup->IsCacheable()) return false;
|
| - if (lookup->holder() == *receiver) return lookup->CanHoldValue(value);
|
| - if (lookup->IsPropertyCallbacks()) return true;
|
| - // JSGlobalProxy either stores on the global object in the prototype, or
|
| - // goes into the runtime if access checks are needed, so this is always
|
| - // safe.
|
| - if (receiver->IsJSGlobalProxy()) {
|
| - PrototypeIterator iter(lookup->isolate(), receiver);
|
| - return lookup->holder() == *PrototypeIterator::GetCurrent(iter);
|
| + it->PrepareTransitionToDataProperty(value, NONE, store_mode);
|
| + return it->IsCacheableTransition();
|
| }
|
| - // Currently normal holders in the prototype chain are not supported. They
|
| - // would require a runtime positive lookup and verification that the details
|
| - // have not changed.
|
| - if (lookup->IsInterceptor() || lookup->IsNormal()) return false;
|
| - holder = Handle<JSObject>(lookup->holder(), lookup->isolate());
|
| - }
|
| -
|
| - // While normally LookupTransition gets passed the receiver, in this case we
|
| - // pass the holder of the property that we overwrite. This keeps the holder in
|
| - // the LookupResult intact so we can later use it to generate a prototype
|
| - // chain check. This avoids a double lookup, but requires us to pass in the
|
| - // receiver when trying to fetch extra information from the transition.
|
| - receiver->map()->LookupTransition(*holder, *name, lookup);
|
| - if (!lookup->IsTransition() || lookup->IsReadOnly()) return false;
|
| -
|
| - // If the value that's being stored does not fit in the field that the
|
| - // instance would transition to, create a new transition that fits the value.
|
| - // This has to be done before generating the IC, since that IC will embed the
|
| - // transition target.
|
| - // Ensure the instance and its map were migrated before trying to update the
|
| - // transition target.
|
| - DCHECK(!receiver->map()->is_deprecated());
|
| - if (!lookup->CanHoldValue(value)) {
|
| - Handle<Map> target(lookup->GetTransitionTarget());
|
| - Representation field_representation = value->OptimalRepresentation();
|
| - Handle<HeapType> field_type = value->OptimalType(
|
| - lookup->isolate(), field_representation);
|
| - Map::GeneralizeRepresentation(
|
| - target, target->LastAdded(),
|
| - field_representation, field_type, FORCE_FIELD);
|
| - // Lookup the transition again since the transition tree may have changed
|
| - // entirely by the migration above.
|
| - receiver->map()->LookupTransition(*holder, *name, lookup);
|
| - if (!lookup->IsTransition()) return false;
|
| - if (!ic->IsNameCompatibleWithPrototypeFailure(name)) return false;
|
| - ic->MarkPrototypeFailure(name);
|
| - return true;
|
| }
|
|
|
| - return true;
|
| + it->PrepareTransitionToDataProperty(value, NONE, store_mode);
|
| + return it->IsCacheableTransition();
|
| }
|
|
|
|
|
| @@ -1399,35 +1336,14 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object,
|
| return result;
|
| }
|
|
|
| - LookupResult lookup(isolate());
|
| - bool can_store = LookupForWrite(object, name, value, &lookup, this);
|
| - if (!can_store &&
|
| - strict_mode() == STRICT &&
|
| - !(lookup.IsProperty() && lookup.IsReadOnly()) &&
|
| - object->IsGlobalObject()) {
|
| - // Strict mode doesn't allow setting non-existent global property.
|
| - return ReferenceError("not_defined", name);
|
| - }
|
| - if (FLAG_use_ic) {
|
| - if (state() == UNINITIALIZED) {
|
| - Handle<Code> stub = pre_monomorphic_stub();
|
| - set_target(*stub);
|
| - TRACE_IC("StoreIC", name);
|
| - } else if (can_store) {
|
| - UpdateCaches(&lookup, Handle<JSObject>::cast(object), name, value);
|
| - } else if (lookup.IsNormal() ||
|
| - (lookup.IsField() && lookup.CanHoldValue(value))) {
|
| - Handle<Code> stub = generic_stub();
|
| - set_target(*stub);
|
| - }
|
| - }
|
| + LookupIterator it(object, name);
|
| + if (FLAG_use_ic) UpdateCaches(&it, value, store_mode);
|
|
|
| // Set the property.
|
| Handle<Object> result;
|
| ASSIGN_RETURN_ON_EXCEPTION(
|
| isolate(), result,
|
| - Object::SetProperty(object, name, value, strict_mode(), store_mode),
|
| - Object);
|
| + Object::SetProperty(&it, value, strict_mode(), store_mode), Object);
|
| return result;
|
| }
|
|
|
| @@ -1475,133 +1391,127 @@ Handle<Code> StoreIC::pre_monomorphic_stub(Isolate* isolate,
|
| }
|
|
|
|
|
| -void StoreIC::UpdateCaches(LookupResult* lookup,
|
| - Handle<JSObject> receiver,
|
| - Handle<Name> name,
|
| - Handle<Object> value) {
|
| - DCHECK(lookup->IsFound());
|
| -
|
| - // These are not cacheable, so we never see such LookupResults here.
|
| - DCHECK(!lookup->IsHandler());
|
| +void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value,
|
| + JSReceiver::StoreFromKeyed store_mode) {
|
| + if (state() == UNINITIALIZED) {
|
| + // This is the first time we execute this inline cache. Set the target to
|
| + // the pre monomorphic stub to delay setting the monomorphic state.
|
| + set_target(*pre_monomorphic_stub());
|
| + TRACE_IC("StoreIC", lookup->name());
|
| + return;
|
| + }
|
|
|
| - Handle<Code> code = ComputeStoreHandler(lookup, receiver, name, value);
|
| + Handle<Code> code = LookupForWrite(lookup, value, store_mode)
|
| + ? ComputeHandler(lookup, value)
|
| + : slow_stub();
|
|
|
| - PatchCache(name, code);
|
| - TRACE_IC("StoreIC", name);
|
| + PatchCache(lookup->name(), code);
|
| + TRACE_IC("StoreIC", lookup->name());
|
| }
|
|
|
|
|
| -Handle<Code> StoreIC::CompileStoreHandler(LookupResult* lookup,
|
| - Handle<Object> object,
|
| - Handle<Name> name,
|
| - Handle<Object> value,
|
| - CacheHolderFlag cache_holder) {
|
| - if (object->IsAccessCheckNeeded()) return slow_stub();
|
| - DCHECK(cache_holder == kCacheOnReceiver || lookup->type() == CALLBACKS ||
|
| - (object->IsJSGlobalProxy() && lookup->holder()->IsJSGlobalObject()));
|
| +Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
|
| + Handle<Object> value,
|
| + CacheHolderFlag cache_holder) {
|
| + DCHECK_NE(LookupIterator::JSPROXY, lookup->state());
|
| +
|
| // This is currently guaranteed by checks in StoreIC::Store.
|
| - Handle<JSObject> receiver = Handle<JSObject>::cast(object);
|
| + Handle<JSObject> receiver = Handle<JSObject>::cast(lookup->GetReceiver());
|
| + Handle<JSObject> holder = lookup->GetHolder<JSObject>();
|
| + DCHECK(!receiver->IsAccessCheckNeeded());
|
|
|
| - Handle<JSObject> holder(lookup->holder());
|
| + // -------------- Transition --------------
|
| + if (lookup->state() == LookupIterator::TRANSITION) {
|
| + Handle<Map> transition = lookup->transition_map();
|
| + // Currently not handled by CompileStoreTransition.
|
| + if (!holder->HasFastProperties()) return slow_stub();
|
|
|
| - if (lookup->IsTransition()) {
|
| - // Explicitly pass in the receiver map since LookupForWrite may have
|
| - // stored something else than the receiver in the holder.
|
| - Handle<Map> transition(lookup->GetTransitionTarget());
|
| - PropertyDetails details = lookup->GetPropertyDetails();
|
| + DCHECK(lookup->IsCacheableTransition());
|
| + NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| + return compiler.CompileStoreTransition(transition, lookup->name());
|
| + }
|
|
|
| - if (details.type() != CALLBACKS && details.attributes() == NONE &&
|
| - holder->HasFastProperties()) {
|
| - NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| - return compiler.CompileStoreTransition(transition, name);
|
| - }
|
| - } else {
|
| - switch (lookup->type()) {
|
| - case FIELD: {
|
| - bool use_stub = true;
|
| - if (lookup->representation().IsHeapObject()) {
|
| - // Only use a generic stub if no types need to be tracked.
|
| - HeapType* field_type = lookup->GetFieldType();
|
| - HeapType::Iterator<Map> it = field_type->Classes();
|
| - use_stub = it.Done();
|
| - }
|
| - if (use_stub) {
|
| - StoreFieldStub stub(isolate(), lookup->GetFieldIndex(),
|
| - lookup->representation());
|
| - return stub.GetCode();
|
| - }
|
| - NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| - return compiler.CompileStoreField(lookup, name);
|
| - }
|
| - case NORMAL:
|
| - if (receiver->IsJSGlobalProxy() || receiver->IsGlobalObject()) {
|
| - // The stub generated for the global object picks the value directly
|
| - // from the property cell. So the property must be directly on the
|
| - // global object.
|
| - PrototypeIterator iter(isolate(), receiver);
|
| - Handle<GlobalObject> global =
|
| - receiver->IsJSGlobalProxy()
|
| - ? Handle<GlobalObject>::cast(
|
| - PrototypeIterator::GetCurrent(iter))
|
| - : Handle<GlobalObject>::cast(receiver);
|
| - Handle<PropertyCell> cell(global->GetPropertyCell(lookup), isolate());
|
| - Handle<HeapType> union_type = PropertyCell::UpdatedType(cell, value);
|
| - StoreGlobalStub stub(
|
| - isolate(), union_type->IsConstant(), receiver->IsJSGlobalProxy());
|
| - Handle<Code> code = stub.GetCodeCopyFromTemplate(global, cell);
|
| - // TODO(verwaest): Move caching of these NORMAL stubs outside as well.
|
| - HeapObject::UpdateMapCodeCache(receiver, name, code);
|
| - return code;
|
| - }
|
| - DCHECK(holder.is_identical_to(receiver));
|
| - return isolate()->builtins()->StoreIC_Normal();
|
| - case CALLBACKS: {
|
| - if (!holder->HasFastProperties()) break;
|
| - Handle<Object> callback(lookup->GetValueFromMap(holder->map()),
|
| - isolate());
|
| - if (callback->IsExecutableAccessorInfo()) {
|
| - Handle<ExecutableAccessorInfo> info =
|
| - Handle<ExecutableAccessorInfo>::cast(callback);
|
| - if (v8::ToCData<Address>(info->setter()) == 0) break;
|
| - if (!ExecutableAccessorInfo::IsCompatibleReceiverType(
|
| - isolate(), info, receiver_type())) {
|
| - break;
|
| - }
|
| - NamedStoreHandlerCompiler compiler(isolate(), receiver_type(),
|
| - holder);
|
| - return compiler.CompileStoreCallback(receiver, name, info);
|
| - } else if (callback->IsAccessorPair()) {
|
| - Handle<Object> setter(
|
| - Handle<AccessorPair>::cast(callback)->setter(), isolate());
|
| - if (!setter->IsJSFunction()) break;
|
| - Handle<JSFunction> function = Handle<JSFunction>::cast(setter);
|
| - CallOptimization call_optimization(function);
|
| - NamedStoreHandlerCompiler compiler(isolate(), receiver_type(),
|
| - holder);
|
| - if (call_optimization.is_simple_api_call() &&
|
| - call_optimization.IsCompatibleReceiver(receiver, holder)) {
|
| - return compiler.CompileStoreCallback(receiver, name,
|
| - call_optimization);
|
| - }
|
| - return compiler.CompileStoreViaSetter(
|
| - receiver, name, Handle<JSFunction>::cast(setter));
|
| - }
|
| - // TODO(dcarney): Handle correctly.
|
| - DCHECK(callback->IsDeclaredAccessorInfo());
|
| - break;
|
| + // -------------- Interceptors --------------
|
| + if (lookup->state() == LookupIterator::INTERCEPTOR) {
|
| + DCHECK(!holder->GetNamedInterceptor()->setter()->IsUndefined());
|
| + NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| + return compiler.CompileStoreInterceptor(lookup->name());
|
| + }
|
| +
|
| + // -------------- Accessors --------------
|
| + DCHECK(lookup->state() == LookupIterator::PROPERTY);
|
| + if (lookup->property_kind() == LookupIterator::ACCESSOR) {
|
| + if (!holder->HasFastProperties()) return slow_stub();
|
| + Handle<Object> accessors = lookup->GetAccessors();
|
| + if (accessors->IsExecutableAccessorInfo()) {
|
| + Handle<ExecutableAccessorInfo> info =
|
| + Handle<ExecutableAccessorInfo>::cast(accessors);
|
| + if (v8::ToCData<Address>(info->setter()) == 0) return slow_stub();
|
| + if (!ExecutableAccessorInfo::IsCompatibleReceiverType(isolate(), info,
|
| + receiver_type())) {
|
| + return slow_stub();
|
| }
|
| - case INTERCEPTOR: {
|
| - DCHECK(HasInterceptorSetter(*holder));
|
| - NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| - return compiler.CompileStoreInterceptor(name);
|
| + NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| + return compiler.CompileStoreCallback(receiver, lookup->name(), info);
|
| + } else if (accessors->IsAccessorPair()) {
|
| + Handle<Object> setter(Handle<AccessorPair>::cast(accessors)->setter(),
|
| + isolate());
|
| + if (!setter->IsJSFunction()) return slow_stub();
|
| + Handle<JSFunction> function = Handle<JSFunction>::cast(setter);
|
| + CallOptimization call_optimization(function);
|
| + NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| + if (call_optimization.is_simple_api_call() &&
|
| + call_optimization.IsCompatibleReceiver(receiver, holder)) {
|
| + return compiler.CompileStoreCallback(receiver, lookup->name(),
|
| + call_optimization);
|
| }
|
| - case CONSTANT:
|
| - break;
|
| - case HANDLER:
|
| - UNREACHABLE();
|
| - break;
|
| + return compiler.CompileStoreViaSetter(receiver, lookup->name(),
|
| + Handle<JSFunction>::cast(setter));
|
| }
|
| + // TODO(dcarney): Handle correctly.
|
| + DCHECK(accessors->IsDeclaredAccessorInfo());
|
| + return slow_stub();
|
| }
|
| +
|
| + // -------------- Dictionary properties --------------
|
| + DCHECK(lookup->property_kind() == LookupIterator::DATA);
|
| + if (lookup->property_encoding() == LookupIterator::DICTIONARY) {
|
| + if (holder->IsGlobalObject()) {
|
| + Handle<PropertyCell> cell = lookup->GetPropertyCell();
|
| + Handle<HeapType> union_type = PropertyCell::UpdatedType(cell, value);
|
| + StoreGlobalStub stub(isolate(), union_type->IsConstant(),
|
| + receiver->IsJSGlobalProxy());
|
| + Handle<Code> code = stub.GetCodeCopyFromTemplate(
|
| + Handle<GlobalObject>::cast(holder), cell);
|
| + // TODO(verwaest): Move caching of these NORMAL stubs outside as well.
|
| + HeapObject::UpdateMapCodeCache(receiver, lookup->name(), code);
|
| + return code;
|
| + }
|
| + DCHECK(holder.is_identical_to(receiver));
|
| + return isolate()->builtins()->StoreIC_Normal();
|
| + }
|
| +
|
| + // -------------- Fields --------------
|
| + DCHECK(lookup->property_encoding() == LookupIterator::DESCRIPTOR);
|
| + if (lookup->property_details().type() == FIELD) {
|
| + bool use_stub = true;
|
| + if (lookup->representation().IsHeapObject()) {
|
| + // Only use a generic stub if no types need to be tracked.
|
| + Handle<HeapType> field_type = lookup->GetFieldType();
|
| + HeapType::Iterator<Map> it = field_type->Classes();
|
| + use_stub = it.Done();
|
| + }
|
| + if (use_stub) {
|
| + StoreFieldStub stub(isolate(), lookup->GetFieldIndex(),
|
| + lookup->representation());
|
| + return stub.GetCode();
|
| + }
|
| + NamedStoreHandlerCompiler compiler(isolate(), receiver_type(), holder);
|
| + return compiler.CompileStoreField(lookup);
|
| + }
|
| +
|
| + // -------------- Constant properties --------------
|
| + DCHECK(lookup->property_details().type() == CONSTANT);
|
| return slow_stub();
|
| }
|
|
|
|
|