StubCache::FindIC(Handle name, Handle stub_holder, Code::Kind kind, ExtraICState extra_state, InlineCacheHolderFlag cache_holder) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder); Handle probe(stub_holder->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); return Handle::null(); } Handle StubCache::FindHandler(Handle name, Handle stub_holder, Code::Kind kind, InlineCacheHolderFlag cache_holder) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::HANDLER, kNoExtraICState, cache_holder, Code::NORMAL, kind); Handle probe(stub_holder->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); return Handle::null(); } Handle StubCache::ComputeMonomorphicIC( Handle name, Handle type, Handle handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); return Handle::null(); } Handle StubCache::FindHandler(Handle name, Handle stub_holder, Code::Kind kind, InlineCacheHolderFlag cache_holder) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::HANDLER, kNoExtraICState, cache_holder, Code::NORMAL, kind); Handle probe(stub_holder->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); return Handle::null(); } Handle StubCache::ComputeMonomorphicIC( Handle name, Handle type, Handle handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::null(); } Handle StubCache::FindHandler(Handle name, Handle stub_holder, Code::Kind kind, InlineCacheHolderFlag cache_holder) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::HANDLER, kNoExtraICState, cache_holder, Code::NORMAL, kind); Handle probe(stub_holder->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); return Handle::null(); } Handle StubCache::ComputeMonomorphicIC( Handle name, Handle type, Handle handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::FindHandler(Handle name, Handle stub_holder, Code::Kind kind, InlineCacheHolderFlag cache_holder) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::HANDLER, kNoExtraICState, cache_holder, Code::NORMAL, kind); Handle probe(stub_holder->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); return Handle::null(); } Handle StubCache::ComputeMonomorphicIC( Handle name, Handle type, Handle handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); return Handle::null(); } Handle StubCache::ComputeMonomorphicIC( Handle name, Handle type, Handle handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::null(); } Handle StubCache::ComputeMonomorphicIC( Handle name, Handle type, Handle handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeMonomorphicIC( Handle name, Handle type, Handle handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
handler, ExtraICState extra_ic_state) { Code::Kind kind = handler->handler_kind(); InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder; Handle ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
ic; // There are multiple string maps that all use the same prototype. That // prototype cannot hold multiple handlers, one for each of the string maps, // for a single name. Hence, turn off caching of the IC. bool can_be_cached = !type->Is(Type::String()); if (can_be_cached) { stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); ic = FindIC(name, stub_holder, kind, extra_ic_state, flag); if (!ic.is_null()) return ic; } if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::KEYED_LOAD_IC) { KeyedLoadStubCompiler ic_compiler(isolate(), flag); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else if (kind == Code::STORE_IC) { StoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } else { ASSERT(kind == Code::KEYED_STORE_IC); ASSERT(STANDARD_STORE == KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); KeyedStoreStubCompiler ic_compiler(isolate(), extra_ic_state); ic = ic_compiler.CompileMonomorphicIC(type, handler, name); } if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); return ic; } Handle StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeLoadNonexistent(Handle name, Handle type) { InlineCacheHolderFlag flag = IC::GetCodeCacheFlag(*type); Handle stub_holder = IC::GetCodeCacheHolder(flag, *type, isolate()); // If no dictionary mode objects are present in the prototype chain, the load // nonexistent IC stub can be shared for all names for a given map and we use // the empty string for the map cache in that case. If there are dictionary // mode objects involved, we need to do negative lookups in the stub and // therefore the stub will be specific to the name. Handle current_map = stub_holder; Handle cache_name = current_map->is_dictionary_map() ? name : Handle::cast(isolate()->factory()->empty_string()); Handle next(current_map->prototype(), isolate()); Handle last = Handle::null(); while (!next->IsNull()) { last = Handle::cast(next); next = handle(current_map->prototype(), isolate()); current_map = handle(Handle::cast(next)->map()); if (current_map->is_dictionary_map()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Handle handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
handler = FindHandler( cache_name, stub_holder, Code::LOAD_IC, flag); if (!handler.is_null()) return handler; LoadStubCompiler compiler(isolate_, flag); handler = compiler.CompileLoadNonexistent(type, last, cache_name); Map::UpdateCodeCache(stub_holder, cache_name, handler); return handler; } Handle StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeKeyedLoadElement(Handle receiver_map) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); Handle name = isolate()->factory()->KeyedLoadElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); KeyedLoadStubCompiler compiler(isolate()); Handle code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileLoadElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); return code; } Handle StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeKeyedStoreElement( Handle receiver_map, StrictModeFlag strict_mode, KeyedAccessStoreMode store_mode) { ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, extra_state); ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle name = isolate()->factory()->KeyedStoreElementMonomorphic_string(); Handle probe(receiver_map->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); KeyedStoreStubCompiler compiler(isolate(), extra_state); Handle code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileStoreElement(receiver_map); Map::UpdateCodeCache(receiver_map, name, code); ASSERT(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) == store_mode); return code; } #define CALL_LOGGER_TAG(kind, type) \ (kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type) Handle StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallConstant(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsSymbol()) { check = SYMBOL_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } if (check != RECEIVER_MAP_CHECK && !function->IsBuiltin() && function->shared()->is_classic_mode()) { // Calling non-strict non-builtins with a value as the receiver // requires boxing. return Handle::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::null(); } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallConstant(object, holder, name, check, function); code->set_check_type(check); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(stub_holder, name, code); } return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallField(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder, PropertyIndex index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT(flags == code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object); Handle stub_holder(IC::GetCodeCacheHolder( isolate_, *object, cache_holder)); // TODO(1233596): We cannot do receiver map check for non-JS objects // because they may be represented as immediates without a // map. Instead, we check against the map in the holder. if (object->IsNumber() || object->IsSymbol() || object->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, cache_holder, Code::FAST, argc); Handle probe(stub_holder->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); HeapObject::UpdateMapCodeCache(stub_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallGlobal(int argc, Code::Kind kind, ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { Code::Flags flags = Code::ComputeMonomorphicFlags( kind, extra_state, OWN_MAP, Code::NORMAL, argc); Handle probe(receiver->map()->FindInCodeCache(*name, flags), isolate_); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT(flags == code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); if (CallStubCompiler::CanBeCached(function)) { HeapObject::UpdateMapCodeCache(receiver, name, code); } return code; } static void FillCache(Isolate* isolate, Handle code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code) { Handle dictionary = UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), code->flags(), code); isolate->heap()->public_set_non_monomorphic_cache(*dictionary); } Code* StubCache::FindCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); UnseededNumberDictionary* dictionary = isolate()->heap()->non_monomorphic_cache(); int entry = dictionary->FindEntry(isolate(), flags); ASSERT(entry != -1); Object* code = dictionary->ValueAt(entry); // This might be called during the marking phase of the collector // hence the unchecked cast. return reinterpret_cast(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT ? CONTEXTUAL : NOT_CONTEXTUAL); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallInitialize(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode) { return ComputeCallInitialize(argc, mode, Code::CALL_IC); } Handle StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallNormal(int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallArguments(int argc) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_CALL_IC, MEGAMORPHIC, kNoExtraICState, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallMiss(int argc, Code::Kind kind, ExtraICState extra_state) { // MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs // and monomorphic stubs are not mixed up together in the stub cache. Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC_PROTOTYPE_FAILURE, extra_state, Code::NORMAL, argc, OWN_MAP); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallMiss(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCompareNil(Handle receiver_map, CompareNilICStub& stub) { Handle name(isolate_->heap()->empty_string()); if (!receiver_map->is_shared()) { Handle cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
cached_ic = FindIC(name, receiver_map, Code::COMPARE_NIL_IC, stub.GetExtraICState()); if (!cached_ic.is_null()) return cached_ic; } Handle ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
ic = stub.GetCodeCopyFromTemplate(isolate_); ic->ReplaceNthObject(1, isolate_->heap()->meta_map(), *receiver_map); if (!receiver_map->is_shared()) { Map::UpdateCodeCache(receiver_map, name, ic); } return ic; } // TODO(verwaest): Change this method so it takes in a TypeHandleList. Handle StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeLoadElementPolymorphic( MapHandleList* receiver_maps) { Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); Handle cache = isolate_->factory()->polymorphic_code_cache(); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); TypeHandleList types(receiver_maps->length()); for (int i = 0; i < receiver_maps->length(); i++) { types.Add(handle(Type::Class(receiver_maps->at(i)), isolate())); } CodeHandleList handlers(receiver_maps->length()); KeyedLoadStubCompiler compiler(isolate_); compiler.CompileElementHandlers(receiver_maps, &handlers); Handle code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompilePolymorphicIC( &types, &handlers, factory()->empty_string(), Code::NORMAL, ELEMENT); isolate()->counters()->keyed_load_polymorphic_stubs()->Increment(); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } Handle StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputePolymorphicIC( TypeHandleList* types, CodeHandleList* handlers, int number_of_valid_types, Handle name, ExtraICState extra_ic_state) { Handle handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
handler = handlers->at(0); Code::Kind kind = handler->handler_kind(); Code::StubType type = number_of_valid_types == 1 ? handler->type() : Code::NORMAL; if (kind == Code::LOAD_IC) { LoadStubCompiler ic_compiler(isolate_); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } else { ASSERT(kind == Code::STORE_IC); StrictModeFlag strict_mode = StoreIC::GetStrictMode(extra_ic_state); StoreStubCompiler ic_compiler(isolate_, strict_mode); return ic_compiler.CompilePolymorphicIC( types, handlers, name, type, PROPERTY); } } Handle StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeStoreElementPolymorphic( MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, StrictModeFlag strict_mode) { ASSERT(store_mode == STANDARD_STORE || store_mode == STORE_AND_GROW_NO_TRANSITION || store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || store_mode == STORE_NO_TRANSITION_HANDLE_COW); Handle cache = isolate_->factory()->polymorphic_code_cache(); ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( strict_mode, store_mode); Code::Flags flags = Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); Handle probe = cache->Lookup(receiver_maps, flags); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
::cast(probe); KeyedStoreStubCompiler compiler(isolate_, extra_state); Handle code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileStoreElementPolymorphic(receiver_maps); PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallDebugBreak(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_BREAK, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallDebugBreak(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCache::ComputeCallDebugPrepareStepIn(int argc, Code::Kind kind) { // Extra IC state is irrelevant for debug break ICs. They jump to // the actual call ic to carry out the work. Code::Flags flags = Code::ComputeFlags(kind, DEBUG_STUB, DEBUG_PREPARE_STEP_IN, Code::NORMAL, argc); Handle cache = isolate_->factory()->non_monomorphic_cache(); int entry = cache->FindEntry(isolate_, flags); if (entry != -1) return Handle(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = compiler.CompileCallDebugPrepareStepIn(flags); FillCache(isolate_, code); return code; } #endif void StubCache::Clear() { Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); for (int i = 0; i < kPrimaryTableSize; i++) { primary_[i].key = heap()->empty_string(); primary_[i].map = NULL; primary_[i].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].map = NULL; secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, Handle name, Code::Flags flags, Handle native_context, Zone* zone) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == *name) { Map* map = primary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; int offset = PrimaryOffset(*name, flags, map); if (entry(primary_, offset) == &primary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == *name) { Map* map = secondary_[i].map; // Map can be NULL, if the stub is constant function call // with a primitive receiver. if (map == NULL) continue; // Lookup in primary table and skip duplicates. int primary_offset = PrimaryOffset(*name, flags, map); // Lookup in secondary table and add matches. int offset = SecondaryOffset(*name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { types->AddMapIfMissing(Handle(map), zone); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); ExecutableAccessorInfo* callback = ExecutableAccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetterCallback fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); ASSERT(callback->IsCompatibleReceiver(recv)); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); // TODO(rossberg): Support symbols in the API. if (name->IsSymbol()) return *value; Handle str = Handle::cast(name); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); PropertyCallbackArguments custom_args(isolate, callback->data(), recv, recv); custom_args.Call(fun, v8::Utils::ToLocal(str), v8::Utils::ToLocal(value)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } /** * Attempts to load a property with an interceptor (which must be present), * but doesn't search the prototype chain. * * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't * provide any value for the given name. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorOnly) { ASSERT(args.length() == StubCache::kInterceptorArgsLength); Handle name_handle = args.at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args.at(StubCache::kInterceptorArgsInfoIndex); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) return isolate->heap()->no_interceptor_result_sentinel(); Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); Handle receiver = args.at(StubCache::kInterceptorArgsThisIndex); Handle holder = args.at(StubCache::kInterceptorArgsHolderIndex); PropertyCallbackArguments callback_args( isolate, interceptor_info->data(), *receiver, *holder); { // Use the interceptor getter. HandleScope scope(isolate); v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(Isolate* isolate, Name* name) { // If the load is non-contextual, just return the undefined result. // Note that both keyed and non-keyed loads may end up here, so we // can't use either LoadIC or KeyedLoadIC constructors. HandleScope scope(isolate); IC ic(IC::NO_EXTRA_FRAME, isolate); ASSERT(ic.IsLoadStub()); if (!ic.SlowIsUndeclaredGlobal()) return isolate->heap()->undefined_value(); // Throw a reference error. Handle name_handle(name); Handle error = isolate->factory()->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return isolate->Throw(*error); } static Handle LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { ASSERT(args->length() == StubCache::kInterceptorArgsLength); Handle name_handle = args->at(StubCache::kInterceptorArgsNameIndex); Handle interceptor_info = args->at(StubCache::kInterceptorArgsInfoIndex); Handle receiver_handle = args->at(StubCache::kInterceptorArgsThisIndex); Handle holder_handle = args->at(StubCache::kInterceptorArgsHolderIndex); Isolate* isolate = receiver_handle->GetIsolate(); // TODO(rossberg): Support symbols in the API. if (name_handle->IsSymbol()) { return JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); } Handle name = Handle::cast(name_handle); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetterCallback getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); PropertyCallbackArguments callback_args(isolate, interceptor_info->data(), *receiver_handle, *holder_handle); { HandleScope scope(isolate); // Use the interceptor getter. v8::Handle r = callback_args.Call(getter, v8::Utils::ToLocal(name)); RETURN_HANDLE_IF_SCHEDULED_EXCEPTION(isolate, Object); if (!r.IsEmpty()) { *attrs = NONE; Handle result = v8::Utils::OpenHandle(*r); result->VerifyApiCallResultType(); return scope.CloseAndEscape(result); } } Handle result = JSObject::GetPropertyPostInterceptor( holder_handle, receiver_handle, name_handle, attrs); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // If the property is present, return it. if (attr != ABSENT) return *result; return ThrowReferenceError(isolate, Name::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; HandleScope scope(isolate); Handle result = LoadWithInterceptor(&args, &attr); RETURN_IF_EMPTY_HANDLE(isolate, result); // This is call IC. In this case, we simply return the undefined result which // will lead to an exception when trying to invoke the result as a // function. return *result; } RUNTIME_FUNCTION(MaybeObject*, StoreInterceptorProperty) { HandleScope scope(isolate); ASSERT(args.length() == 3); StoreIC ic(IC::NO_EXTRA_FRAME, isolate); Handle receiver = args.at(0); Handle name = args.at(1); Handle value = args.at(2); ASSERT(receiver->HasNamedInterceptor()); PropertyAttributes attr = NONE; Handle result = JSObject::SetPropertyWithInterceptor( receiver, name, value, attr, ic.strict_mode()); RETURN_IF_EMPTY_HANDLE(isolate, result); return *result; } RUNTIME_FUNCTION(MaybeObject*, KeyedLoadPropertyWithInterceptor) { JSObject* receiver = JSObject::cast(args[0]); ASSERT(args.smi_at(1) >= 0); uint32_t index = args.smi_at(1); return receiver->GetElementWithInterceptor(receiver, index); } Handle StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallInitialize"); isolate()->counters()->call_initialize_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_INITIALIZE_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_INITIALIZE, *code)); return code; } Handle StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); // The code of the PreMonomorphic stub is the same as the code // of the Initialized stub. They just differ on the code object flags. Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateInitialize(masm(), argc, extra_state); } else { KeyedCallIC::GenerateInitialize(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallPreMonomorphic"); isolate()->counters()->call_premonomorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_PRE_MONOMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_PRE_MONOMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallNormal(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // Call normal is always with a explict receiver. ASSERT(!CallIC::Contextual::decode( Code::ExtractExtraICStateFromFlags(flags))); CallIC::GenerateNormal(masm(), argc); } else { KeyedCallIC::GenerateNormal(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallNormal"); isolate()->counters()->call_normal_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_NORMAL_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_NORMAL, *code)); return code; } Handle StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallMegamorphic(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMegamorphic(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMegamorphic(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallMegamorphic"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallArguments(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); KeyedCallIC::GenerateNonStrictArguments(masm(), argc); Handle code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallArguments"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_MEGAMORPHIC_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MEGAMORPHIC, *code)); return code; } Handle StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallMiss(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); if (kind == Code::CALL_IC) { CallIC::GenerateMiss(masm(), argc, extra_state); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallMiss"); isolate()->counters()->call_megamorphic_stubs()->Increment(); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_MISS_TAG), *code, code->arguments_count())); GDBJIT(AddCode(GDBJITInterface::CALL_MISS, *code)); return code; } #ifdef ENABLE_DEBUGGER_SUPPORT Handle StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallDebugBreak(Code::Flags flags) { Debug::GenerateCallICDebugBreak(masm()); Handle code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallDebugBreak"); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(Code::ExtractKindFromFlags(flags), CALL_DEBUG_BREAK_TAG), *code, code->arguments_count())); return code; } Handle StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { // Use the same code for the the step in preparations as we do for the // miss case. int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); if (kind == Code::CALL_IC) { // For the debugger extra ic state is irrelevant. CallIC::GenerateMiss(masm(), argc, kNoExtraICState); } else { KeyedCallIC::GenerateMiss(masm(), argc); } Handle code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn"); PROFILE(isolate(), CodeCreateEvent( CALL_LOGGER_TAG(kind, CALL_DEBUG_PREPARE_STEP_IN_TAG), *code, code->arguments_count())); return code; } #endif // ENABLE_DEBUGGER_SUPPORT #undef CALL_LOGGER_TAG Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Handle code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = factory()->NewCode(desc, flags, masm_.CodeObject()); if (code->has_major_key()) { code->set_major_key(CodeStub::NoCache); } #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code_stubs) code->Disassemble(name); #endif return code; } Handle StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
StubCompiler::GetCodeWithFlags(Code::Flags flags, Handle name) { return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) ? GetCodeWithFlags(flags, *Handle::cast(name)->ToCString()) : GetCodeWithFlags(flags, NULL); } void StubCompiler::LookupPostInterceptor(Handle holder, Handle name, LookupResult* lookup) { holder->LocalLookupRealNamedProperty(*name, lookup); if (lookup->IsFound()) return; if (holder->GetPrototype()->IsNull()) return; holder->GetPrototype()->Lookup(*name, lookup); } #define __ ACCESS_MASM(masm()) CallKind CallStubCompiler::call_kind() { return CallICBase::Contextual::decode(extra_state()) ? CALL_AS_FUNCTION : CALL_AS_METHOD; } void CallStubCompiler::HandlerFrontendFooter(Label* miss) { __ bind(miss); GenerateMissBranch(); } void CallStubCompiler::GenerateJumpFunctionIgnoreReceiver( Handle function) { ParameterCount expected(function); __ InvokeFunction(function, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } void CallStubCompiler::GenerateJumpFunction(Handle object, Handle function) { PatchGlobalProxy(object); GenerateJumpFunctionIgnoreReceiver(function); } void CallStubCompiler::GenerateJumpFunction(Handle object, Register actual_closure, Handle function) { PatchGlobalProxy(object); ParameterCount expected(function); __ InvokeFunction(actual_closure, expected, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind()); } Handle CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
CallStubCompiler::CompileCallConstant( Handle object, Handle holder, Handle name, CheckType check, Handle function) { if (HasCustomCallGenerator(function)) { Handle code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
code = CompileCustomCall(object, holder, Handle::null(), function, Handle::cast(name), Code::FAST); // A null handle means bail out to the regular compiler code below. if (!code.is_null()) return code; } Label miss; HandlerFrontendHeader(object, holder, name, check, &miss); GenerateJumpFunction(object, function); HandlerFrontendFooter(&miss); // Return the generated code. return GetCode(function); } Register LoadStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { PrototypeCheckType check_type = CHECK_ALL_MAPS; int function_index = -1; if (type->Is(Type::String())) { function_index = Context::STRING_FUNCTION_INDEX; } else if (type->Is(Type::Symbol())) { function_index = Context::SYMBOL_FUNCTION_INDEX; } else if (type->Is(Type::Number())) { function_index = Context::NUMBER_FUNCTION_INDEX; } else if (type->Is(Type::Boolean())) { // Booleans use the generic oddball map, so an additional check is needed to // ensure the receiver is really a boolean. GenerateBooleanCheck(object_reg, miss); function_index = Context::BOOLEAN_FUNCTION_INDEX; } else { check_type = SKIP_RECEIVER; } if (check_type == CHECK_ALL_MAPS) { GenerateDirectLoadGlobalFunctionPrototype( masm(), function_index, scratch1(), miss); Object* function = isolate()->native_context()->get(function_index); Object* prototype = JSFunction::cast(function)->instance_prototype(); type = IC::CurrentTypeOf(handle(prototype, isolate()), isolate()); object_reg = scratch1(); } // Check that the maps starting from the prototype haven't changed. return CheckPrototypes( type, object_reg, holder, scratch1(), scratch2(), scratch3(), name, miss, check_type); } // HandlerFrontend for store uses the name register. It has to be restored // before a miss. Register StoreStubCompiler::HandlerFrontendHeader( Handle type, Register object_reg, Handle holder, Handle name, Label* miss) { return CheckPrototypes(type, object_reg, holder, this->name(), scratch1(), scratch2(), name, miss, SKIP_RECEIVER); } bool BaseLoadStoreStubCompiler::IncludesNumberType(TypeHandleList* types) { for (int i = 0; i < types->length(); ++i) { if (types->at(i)->Is(Type::Number())) return true; } return false; } Register BaseLoadStoreStubCompiler::HandlerFrontend(Handle type, Register object_reg, Handle holder, Handle name) { Label miss; Register reg = HandlerFrontendHeader(type, object_reg, holder, name, &miss); HandlerFrontendFooter(name, &miss); return reg; } void LoadStubCompiler::NonexistentHandlerFrontend(Handle type, Handle last, Handle name) { Label miss; Register holder; Handle last_map; if (last.is_null()) { holder = receiver(); last_map = IC::TypeToMap(*type, isolate()); // If |type| has null as its prototype, |last| is Handle::null(). ASSERT(last_map->prototype() == isolate()->heap()->null_value()); } else { holder = HandlerFrontendHeader(type, receiver(), last, name, &miss); last_map = handle(last->map()); } if (last_map->is_dictionary_map() && !last_map->IsJSGlobalObjectMap() && !last_map->IsJSGlobalProxyMap()) { if (!name->IsUniqueName()) { ASSERT(name->IsString()); name = factory()->InternalizeString(Handle::cast(name)); } ASSERT(last.is_null() || last->property_dictionary()->FindEntry(*name) == NameDictionary::kNotFound); GenerateDictionaryNegativeLookup(masm(), &miss, holder, name, scratch2(), scratch3()); } // If the last object in the prototype chain is a global object, // check that the global property cell is empty. if (last_map->IsJSGlobalObjectMap()) { Handle global = last.is_null() ? Handle::cast(type->AsConstant()) : Handle::cast(last); GenerateCheckPropertyCell(masm(), global, name, scratch2(), &miss); } HandlerFrontendFooter(name, &miss); } Handle LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
LoadStubCompiler::CompileLoadField( Handle type, Handle holder, Handle name, PropertyIndex field, Representation representation) { Label miss; Register reg = HandlerFrontendHeader(type, receiver(), holder, name, &miss); GenerateLoadField(reg, holder, field, representation); __ bind(&miss); TailCallBuiltin(masm(), MissBuiltin(kind())); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
LoadStubCompiler::CompileLoadConstant( Handle type, Handle holder, Handle name, Handle value) { HandlerFrontend(type, receiver(), holder, name); GenerateLoadConstant(value); // Return the generated code. return GetCode(kind(), Code::FAST, name); } Handle LoadStubCompiler::CompileLoadCallback( Handle type, Handle
LoadStubCompiler::CompileLoadCallback( Handle type, Handle