// Copyright 2014 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/ic/handler-compiler.h" #include "src/field-type.h" #include "src/ic/call-optimization.h" #include "src/ic/handler-configuration-inl.h" #include "src/ic/ic-inl.h" #include "src/ic/ic.h" #include "src/isolate-inl.h" namespace v8 { namespace internal { Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name, Handle<Map> stub_holder, Code::Kind kind, CacheHolderFlag cache_holder) { Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder); Code* code = stub_holder->LookupInCodeCache(*name, flags); if (code == nullptr) return Handle<Code>(); return handle(code); } Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind, Handle<Name> name) { Code::Flags flags = Code::ComputeHandlerFlags(kind, cache_holder()); Handle<Code> code = GetCodeWithFlags(flags, name); PROFILE(isolate(), CodeCreateEvent(CodeEventListener::HANDLER_TAG, AbstractCode::cast(*code), *name)); #ifdef DEBUG code->VerifyEmbeddedObjects(); #endif return code; } #define __ ACCESS_MASM(masm()) Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg, Handle<Name> name, Label* miss, ReturnHolder return_what) { if (map()->IsPrimitiveMap() || map()->IsJSGlobalProxyMap()) { // If the receiver is a global proxy and if we get to this point then // the compile-time (current) native context has access to global proxy's // native context. Since access rights revocation is not supported at all, // we can generate a check that an execution-time native context is either // the same as compile-time native context or has the same access token. Handle<Context> native_context = isolate()->native_context(); Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate()); bool compare_native_contexts_only = map()->IsPrimitiveMap(); GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss, compare_native_contexts_only); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name, miss, return_what); } // Frontend for store uses the name register. It has to be restored before a // miss. Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg, Handle<Name> name, Label* miss, ReturnHolder return_what) { if (map()->IsJSGlobalProxyMap()) { Handle<Context> native_context = isolate()->native_context(); Handle<WeakCell> weak_cell(native_context->self_weak_cell(), isolate()); GenerateAccessCheck(weak_cell, scratch1(), scratch2(), miss, false); } return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name, miss, return_what); } Register PropertyHandlerCompiler::Frontend(Handle<Name> name) { Label miss; if (IC::ShouldPushPopSlotAndVector(kind())) { PushVectorAndSlot(); } Register reg = FrontendHeader(receiver(), name, &miss, RETURN_HOLDER); FrontendFooter(name, &miss); // The footer consumes the vector and slot from the stack if miss occurs. if (IC::ShouldPushPopSlotAndVector(kind())) { DiscardVectorAndSlot(); } return reg; } Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( Handle<Name> name, Handle<AccessorInfo> callback, Handle<Code> slow_stub) { if (V8_UNLIKELY(FLAG_runtime_stats)) { GenerateTailCall(masm(), slow_stub); } Register reg = Frontend(name); GenerateLoadCallback(reg, callback); return GetCode(kind(), name); } Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( Handle<Name> name, const CallOptimization& call_optimization, int accessor_index, Handle<Code> slow_stub) { DCHECK(call_optimization.is_simple_api_call()); if (V8_UNLIKELY(FLAG_runtime_stats)) { GenerateTailCall(masm(), slow_stub); } Register holder = Frontend(name); GenerateApiAccessorCall(masm(), call_optimization, map(), receiver(), scratch2(), false, no_reg, holder, accessor_index); return GetCode(kind(), name); } void NamedLoadHandlerCompiler::InterceptorVectorSlotPush(Register holder_reg) { if (IC::ShouldPushPopSlotAndVector(kind())) { if (holder_reg.is(receiver())) { PushVectorAndSlot(); } else { DCHECK(holder_reg.is(scratch1())); PushVectorAndSlot(scratch2(), scratch3()); } } } void NamedLoadHandlerCompiler::InterceptorVectorSlotPop(Register holder_reg, PopMode mode) { if (IC::ShouldPushPopSlotAndVector(kind())) { if (mode == DISCARD) { DiscardVectorAndSlot(); } else { if (holder_reg.is(receiver())) { PopVectorAndSlot(); } else { DCHECK(holder_reg.is(scratch1())); PopVectorAndSlot(scratch2(), scratch3()); } } } } Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor( LookupIterator* it) { // So far the most popular follow ups for interceptor loads are DATA and // AccessorInfo, so inline only them. Other cases may be added // later. bool inline_followup = false; switch (it->state()) { case LookupIterator::TRANSITION: UNREACHABLE(); case LookupIterator::ACCESS_CHECK: case LookupIterator::INTERCEPTOR: case LookupIterator::JSPROXY: case LookupIterator::NOT_FOUND: case LookupIterator::INTEGER_INDEXED_EXOTIC: break; case LookupIterator::DATA: { PropertyDetails details = it->property_details(); inline_followup = details.kind() == kData && details.location() == kField && !it->is_dictionary_holder(); break; } case LookupIterator::ACCESSOR: { Handle<Object> accessors = it->GetAccessors(); if (accessors->IsAccessorInfo()) { Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(accessors); inline_followup = info->getter() != NULL && AccessorInfo::IsCompatibleReceiverMap(isolate(), info, map()); } else if (accessors->IsAccessorPair()) { Handle<JSObject> property_holder(it->GetHolder<JSObject>()); Handle<Object> getter(Handle<AccessorPair>::cast(accessors)->getter(), isolate()); if (!(getter->IsJSFunction() || getter->IsFunctionTemplateInfo())) { break; } if (!property_holder->HasFastProperties()) break; CallOptimization call_optimization(getter); Handle<Map> receiver_map = map(); inline_followup = call_optimization.is_simple_api_call() && call_optimization.IsCompatibleReceiverMap( receiver_map, property_holder); } } } Label miss; InterceptorVectorSlotPush(receiver()); bool lost_holder_register = false; auto holder_orig = holder(); // non masking interceptors must check the entire chain, so temporarily reset // the holder to be that last element for the FrontendHeader call. if (holder()->GetNamedInterceptor()->non_masking()) { DCHECK(!inline_followup); JSObject* last = *holder(); PrototypeIterator iter(isolate(), last); while (!iter.IsAtEnd()) { lost_holder_register = true; // Casting to JSObject is fine here. The LookupIterator makes sure to // look behind non-masking interceptors during the original lookup, and // we wouldn't try to compile a handler if there was a Proxy anywhere. last = iter.GetCurrent<JSObject>(); iter.Advance(); } auto last_handle = handle(last); set_holder(last_handle); } Register reg = FrontendHeader(receiver(), it->name(), &miss, RETURN_HOLDER); // Reset the holder so further calculations are correct. set_holder(holder_orig); if (lost_holder_register) { if (*it->GetReceiver() == *holder()) { reg = receiver(); } else { // Reload lost holder register. auto cell = isolate()->factory()->NewWeakCell(holder()); __ LoadWeakValue(reg, cell, &miss); } } FrontendFooter(it->name(), &miss); InterceptorVectorSlotPop(reg); if (inline_followup) { // TODO(368): Compile in the whole chain: all the interceptors in // prototypes and ultimate answer. GenerateLoadInterceptorWithFollowup(it, reg); } else { GenerateLoadInterceptor(reg); } return GetCode(kind(), it->name()); } void NamedLoadHandlerCompiler::GenerateLoadCallback( Register reg, Handle<AccessorInfo> callback) { DCHECK(receiver().is(ApiGetterDescriptor::ReceiverRegister())); __ Move(ApiGetterDescriptor::HolderRegister(), reg); // The callback is alive if this instruction is executed, // so the weak cell is not cleared and points to data. Handle<WeakCell> cell = isolate()->factory()->NewWeakCell(callback); __ GetWeakValue(ApiGetterDescriptor::CallbackRegister(), cell); CallApiGetterStub stub(isolate()); __ TailCallStub(&stub); } void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor( LookupIterator* it, Register interceptor_reg) { Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>()); Handle<Map> holder_map(holder()->map()); set_map(holder_map); set_holder(real_named_property_holder); Label miss; InterceptorVectorSlotPush(interceptor_reg); Register reg = FrontendHeader(interceptor_reg, it->name(), &miss, RETURN_HOLDER); FrontendFooter(it->name(), &miss); // We discard the vector and slot now because we don't miss below this point. InterceptorVectorSlotPop(reg, DISCARD); switch (it->state()) { case LookupIterator::ACCESS_CHECK: case LookupIterator::INTERCEPTOR: case LookupIterator::JSPROXY: case LookupIterator::NOT_FOUND: case LookupIterator::INTEGER_INDEXED_EXOTIC: case LookupIterator::TRANSITION: UNREACHABLE(); case LookupIterator::DATA: { DCHECK_EQ(kData, it->property_details().kind()); DCHECK_EQ(kField, it->property_details().location()); __ Move(LoadFieldDescriptor::ReceiverRegister(), reg); Handle<Object> smi_handler = LoadIC::SimpleFieldLoad(isolate(), it->GetFieldIndex()); __ Move(LoadFieldDescriptor::SmiHandlerRegister(), smi_handler); GenerateTailCall(masm(), isolate()->builtins()->LoadField()); break; } case LookupIterator::ACCESSOR: if (it->GetAccessors()->IsAccessorInfo()) { Handle<AccessorInfo> info = Handle<AccessorInfo>::cast(it->GetAccessors()); DCHECK_NOT_NULL(info->getter()); GenerateLoadCallback(reg, info); } else { Handle<Object> function = handle( AccessorPair::cast(*it->GetAccessors())->getter(), isolate()); CallOptimization call_optimization(function); GenerateApiAccessorCall(masm(), call_optimization, holder_map, receiver(), scratch2(), false, no_reg, reg, it->GetAccessorIndex()); } } } Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter( Handle<Name> name, int accessor_index, int expected_arguments) { Register holder = Frontend(name); GenerateLoadViaGetter(masm(), map(), receiver(), holder, accessor_index, expected_arguments, scratch2()); return GetCode(kind(), name); } Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter( Handle<JSObject> object, Handle<Name> name, int accessor_index, int expected_arguments) { Register holder = Frontend(name); GenerateStoreViaSetter(masm(), map(), receiver(), holder, accessor_index, expected_arguments, scratch2()); return GetCode(kind(), name); } Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( Handle<JSObject> object, Handle<Name> name, const CallOptimization& call_optimization, int accessor_index, Handle<Code> slow_stub) { if (V8_UNLIKELY(FLAG_runtime_stats)) { GenerateTailCall(masm(), slow_stub); } Register holder = Frontend(name); if (Descriptor::kPassLastArgsOnStack) { __ LoadParameterFromStack<Descriptor>(value(), Descriptor::kValue); } GenerateApiAccessorCall(masm(), call_optimization, handle(object->map()), receiver(), scratch2(), true, value(), holder, accessor_index); return GetCode(kind(), name); } #undef __ // static Handle<Object> ElementHandlerCompiler::GetKeyedLoadHandler( Handle<Map> receiver_map, Isolate* isolate) { if (receiver_map->has_indexed_interceptor() && !receiver_map->GetIndexedInterceptor()->getter()->IsUndefined(isolate) && !receiver_map->GetIndexedInterceptor()->non_masking()) { TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedInterceptorStub); return LoadIndexedInterceptorStub(isolate).GetCode(); } if (receiver_map->IsStringMap()) { TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadIndexedStringStub); return isolate->builtins()->KeyedLoadIC_IndexedString(); } InstanceType instance_type = receiver_map->instance_type(); if (instance_type < FIRST_JS_RECEIVER_TYPE) { TRACE_HANDLER_STATS(isolate, KeyedLoadIC_SlowStub); return isolate->builtins()->KeyedLoadIC_Slow(); } ElementsKind elements_kind = receiver_map->elements_kind(); if (IsSloppyArgumentsElements(elements_kind)) { TRACE_HANDLER_STATS(isolate, KeyedLoadIC_KeyedLoadSloppyArgumentsStub); return KeyedLoadSloppyArgumentsStub(isolate).GetCode(); } bool is_js_array = instance_type == JS_ARRAY_TYPE; if (elements_kind == DICTIONARY_ELEMENTS) { TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH); return LoadHandler::LoadElement(isolate, elements_kind, false, is_js_array); } DCHECK(IsFastElementsKind(elements_kind) || IsFixedTypedArrayElementsKind(elements_kind)); // TODO(jkummerow): Use IsHoleyElementsKind(elements_kind). bool convert_hole_to_undefined = is_js_array && elements_kind == FAST_HOLEY_ELEMENTS && *receiver_map == isolate->get_initial_js_array_map(elements_kind); TRACE_HANDLER_STATS(isolate, KeyedLoadIC_LoadElementDH); return LoadHandler::LoadElement(isolate, elements_kind, convert_hole_to_undefined, is_js_array); } void ElementHandlerCompiler::CompileElementHandlers( MapHandleList* receiver_maps, List<Handle<Object>>* handlers) { for (int i = 0; i < receiver_maps->length(); ++i) { handlers->Add(GetKeyedLoadHandler(receiver_maps->at(i), isolate())); } } } // namespace internal } // namespace v8