StubCache::ComputeLoadNonexistent(Handle name, Handle receiver) { ASSERT(receiver->IsGlobalObject() || receiver->HasFastProperties()); // If no global 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 global objects involved, we need to check global // property cells in the stub and therefore the stub will be // specific to the name. Handle cache_name = factory()->empty_string(); if (receiver->IsGlobalObject()) cache_name = name; Handle last = receiver; while (last->GetPrototype() != heap()->null_value()) { last = Handle(JSObject::cast(last->GetPrototype())); if (last->IsGlobalObject()) cache_name = name; } // Compile the stub that is either shared for all names or // name specific if there are global objects involved. Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NONEXISTENT); Handle probe(receiver->map()->FindInCodeCache(*cache_name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadNonexistent(cache_name, receiver, last); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *cache_name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *cache_name, *code)); JSObject::UpdateMapCodeCache(receiver, cache_name, code); return code; } Handle StubCache::ComputeLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(receiver, holder, field_index, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadCallback(Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(v8::ToCData(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadNonexistent(cache_name, receiver, last); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *cache_name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *cache_name, *code)); JSObject::UpdateMapCodeCache(receiver, cache_name, code); return code; } Handle StubCache::ComputeLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(receiver, holder, field_index, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadCallback(Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(v8::ToCData(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadNonexistent(cache_name, receiver, last); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *cache_name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *cache_name, *code)); JSObject::UpdateMapCodeCache(receiver, cache_name, code); return code; } Handle StubCache::ComputeLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(receiver, holder, field_index, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadCallback(Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(v8::ToCData(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(receiver, holder, field_index, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadCallback(Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(v8::ToCData(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(receiver, holder, field_index, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadCallback(Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(v8::ToCData(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadField(receiver, holder, field_index, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadCallback(Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(v8::ToCData(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeLoadCallback(Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(v8::ToCData(callback->getter()) != 0); ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadConstant(receiver, holder, value, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeLoadNormal() { return isolate_->builtins()->LoadIC_Normal(); } Handle StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeLoadGlobal(Handle name, Handle receiver, Handle holder, Handle cell, bool is_dont_delete) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); LoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadGlobal(receiver, holder, cell, name, is_dont_delete); PROFILE(isolate_, CodeCreateEvent(Logger::LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadField(Handle name, Handle receiver, Handle holder, int field_index) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadField(name, receiver, holder, field_index); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadConstant(Handle name, Handle receiver, Handle holder, Handle value) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadConstant(name, receiver, holder, value); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadInterceptor(Handle name, Handle receiver, Handle holder) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadInterceptor(receiver, holder, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadCallback( Handle name, Handle receiver, Handle holder, Handle callback) { ASSERT(IC::GetCodeCacheForObject(*receiver, *holder) == OWN_MAP); Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadCallback(name, receiver, holder, callback); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadArrayLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadArrayLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadStringLength(Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle map(receiver->map()); Handle probe(map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadStringLength(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); Map::UpdateCodeCache(map, name, code); return code; } Handle StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadFunctionPrototype( Handle name, Handle receiver) { Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedLoadStubCompiler compiler(isolate_); Handle code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileLoadFunctionPrototype(name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_LOAD_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedLoadOrStoreElement( Handle receiver, KeyedIC::StubKind stub_kind, StrictModeFlag strict_mode) { KeyedAccessGrowMode grow_mode = KeyedIC::GetGrowModeFromStubKind(stub_kind); Code::ExtraICState extra_state = Code::ComputeExtraICState(grow_mode, strict_mode); Code::Flags flags = Code::ComputeMonomorphicFlags( stub_kind == KeyedIC::LOAD ? Code::KEYED_LOAD_IC : Code::KEYED_STORE_IC, NORMAL, extra_state); Handle name; switch (stub_kind) { case KeyedIC::LOAD: name = isolate()->factory()->KeyedLoadElementMonomorphic_symbol(); break; case KeyedIC::STORE_NO_TRANSITION: name = isolate()->factory()->KeyedStoreElementMonomorphic_symbol(); break; case KeyedIC::STORE_AND_GROW_NO_TRANSITION: name = isolate()->factory()->KeyedStoreAndGrowElementMonomorphic_symbol(); break; default: UNREACHABLE(); break; } Handle receiver_map(receiver->map()); Handle probe(receiver_map->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); Handle code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code; switch (stub_kind) { case KeyedIC::LOAD: { KeyedLoadStubCompiler compiler(isolate_); code = compiler.CompileLoadElement(receiver_map); break; } case KeyedIC::STORE_AND_GROW_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } case KeyedIC::STORE_NO_TRANSITION: { KeyedStoreStubCompiler compiler(isolate_, strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); code = compiler.CompileStoreElement(receiver_map); break; } default: UNREACHABLE(); break; } ASSERT(!code.is_null()); if (stub_kind == KeyedIC::LOAD) { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, *code, 0)); } else { PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, 0)); } JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeStoreNormal(StrictModeFlag strict_mode) { return (strict_mode == kStrictMode) ? isolate_->builtins()->Builtins::StoreIC_Normal_Strict() : isolate_->builtins()->Builtins::StoreIC_Normal(); } Handle StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeStoreGlobal(Handle name, Handle receiver, Handle cell, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, NORMAL, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileStoreGlobal(receiver, cell, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeStoreCallback(Handle name, Handle receiver, Handle callback, StrictModeFlag strict_mode) { ASSERT(v8::ToCData(callback->setter()) != 0); Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, CALLBACKS, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileStoreCallback(receiver, callback, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeStoreInterceptor(Handle name, Handle receiver, StrictModeFlag strict_mode) { Code::Flags flags = Code::ComputeMonomorphicFlags( Code::STORE_IC, INTERCEPTOR, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); StoreStubCompiler compiler(isolate_, strict_mode); Handle code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileStoreInterceptor(receiver, name); PROFILE(isolate_, CodeCreateEvent(Logger::STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); return code; } Handle StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedStoreField(Handle name, Handle receiver, int field_index, Handle transition, StrictModeFlag strict_mode) { PropertyType type = (transition.is_null()) ? FIELD : MAP_TRANSITION; Code::Flags flags = Code::ComputeMonomorphicFlags( Code::KEYED_STORE_IC, type, strict_mode); Handle probe(receiver->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); KeyedStoreStubCompiler compiler(isolate(), strict_mode, DO_NOT_ALLOW_JSARRAY_GROWTH); Handle code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileStoreField(receiver, field_index, transition, name); PROFILE(isolate_, CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, *code, *name)); GDBJIT(AddCode(GDBJITInterface::KEYED_STORE_IC, *name, *code)); JSObject::UpdateMapCodeCache(receiver, name, code); 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, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallConstant(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, Handle function) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*object, cache_holder)); // Compute check type based on receiver/holder. CheckType check = RECEIVER_MAP_CHECK; if (object->IsString()) { check = STRING_CHECK; } else if (object->IsNumber()) { check = NUMBER_CHECK; } else if (object->IsBoolean()) { check = BOOLEAN_CHECK; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, CONSTANT_FUNCTION, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallConstant(object, holder, function, name, check); code->set_check_type(check); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallField(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder, int index) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, FIELD, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); CallStubCompiler compiler(isolate_, argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallField(Handle::cast(object), holder, index, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate_, CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallInterceptor(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle object, Handle holder) { // Compute the check type and the map. InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*object, *holder); Handle map_holder(IC::GetCodeCacheHolder(*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->IsBoolean() || object->IsString()) { object = holder; } Code::Flags flags = Code::ComputeMonomorphicFlags(kind, INTERCEPTOR, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); 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_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallInterceptor(Handle::cast(object), holder, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, name, code); return code; } Handle StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallGlobal(int argc, Code::Kind kind, Code::ExtraICState extra_state, Handle name, Handle receiver, Handle holder, Handle cell, Handle function) { InlineCacheHolderFlag cache_holder = IC::GetCodeCacheForObject(*receiver, *holder); Handle map_holder(IC::GetCodeCacheHolder(*receiver, cache_holder)); Code::Flags flags = Code::ComputeMonomorphicFlags(kind, NORMAL, extra_state, cache_holder, argc); Handle probe(map_holder->map()->FindInCodeCache(*name, flags)); if (probe->IsCode()) return Handle::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
::cast(probe); CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder); Handle code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallGlobal(receiver, holder, cell, function, name); ASSERT_EQ(flags, code->flags()); PROFILE(isolate(), CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name)); GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code)); JSObject::UpdateMapCodeCache(map_holder, 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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); // Use raw_unchecked... so we don't get assert failures during GC. UnseededNumberDictionary* dictionary = isolate()->heap()->raw_unchecked_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) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(code); } Handle StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallInitialize(int argc, RelocInfo::Mode mode, Code::Kind kind) { Code::ExtraICState extra_state = CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) | CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT); Code::Flags flags = Code::ComputeFlags(kind, UNINITIALIZED, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeKeyedCallInitialize(int argc) { return ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC); } Handle StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallPreMonomorphic( int argc, Code::Kind kind, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallPreMonomorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallNormal(int argc, Code::Kind kind, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallNormal(int argc, Code::Kind kind, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MONOMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc, Code::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallNormal(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallArguments(int argc, Code::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallArguments(int argc, Code::Kind kind) { ASSERT(kind == Code::KEYED_CALL_IC); Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(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, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallArguments(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallMegamorphic( int argc, Code::Kind kind, Code::ExtraICState extra_state) { Code::Flags flags = Code::ComputeFlags(kind, MEGAMORPHIC, extra_state, NORMAL, argc); Handle cache = isolate_->factory()->non_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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(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, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallMegamorphic(flags); FillCache(isolate_, code); return code; } Handle StubCache::ComputeCallMiss(int argc, Code::Kind kind, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
StubCache::ComputeCallMiss(int argc, Code::Kind kind, Code::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, 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; } #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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(Code::cast(cache->ValueAt(entry))); StubCompiler compiler(isolate_); Handle code = compiler.CompileCallMiss(flags); FillCache(isolate_, 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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
code = compiler.CompileCallMiss(flags); FillCache(isolate_, 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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
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_BREAK, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
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_PREPARE_STEP_IN, Code::kNoExtraICState, NORMAL, argc); Handle cache = isolate_->factory()->non_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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
(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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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
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].value = empty; } for (int j = 0; j < kSecondaryTableSize; j++) { secondary_[j].key = heap()->empty_string(); secondary_[j].value = empty; } } void StubCache::CollectMatchingMaps(SmallMapList* types, String* name, Code::Flags flags, Handle global_context) { for (int i = 0; i < kPrimaryTableSize; i++) { if (primary_[i].key == name) { Map* map = primary_[i].value->FindFirstMap(); // 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, *global_context)) { types->Add(Handle(map)); } } } for (int i = 0; i < kSecondaryTableSize; i++) { if (secondary_[i].key == name) { Map* map = secondary_[i].value->FindFirstMap(); // 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); Entry* primary_entry = entry(primary_, primary_offset); if (primary_entry->key == name) { Map* primary_map = primary_entry->value->FindFirstMap(); if (map == primary_map) continue; } // Lookup in secondary table and add matches. int offset = SecondaryOffset(name, flags, primary_offset); if (entry(secondary_, offset) == &secondary_[i] && !TypeFeedbackOracle::CanRetainOtherContext(map, *global_context)) { types->Add(Handle(map)); } } } } // ------------------------------------------------------------------------ // StubCompiler implementation. RUNTIME_FUNCTION(MaybeObject*, LoadCallbackProperty) { ASSERT(args[0]->IsJSObject()); ASSERT(args[1]->IsJSObject()); AccessorInfo* callback = AccessorInfo::cast(args[3]); Address getter_address = v8::ToCData(callback->getter()); v8::AccessorGetter fun = FUNCTION_CAST(getter_address); ASSERT(fun != NULL); v8::AccessorInfo info(&args[0]); HandleScope scope(isolate); v8::Handle result; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, getter_address); result = fun(v8::Utils::ToLocal(args.at(4)), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (result.IsEmpty()) return HEAP->undefined_value(); return *v8::Utils::OpenHandle(*result); } RUNTIME_FUNCTION(MaybeObject*, StoreCallbackProperty) { JSObject* recv = JSObject::cast(args[0]); AccessorInfo* callback = AccessorInfo::cast(args[1]); Address setter_address = v8::ToCData(callback->setter()); v8::AccessorSetter fun = FUNCTION_CAST(setter_address); ASSERT(fun != NULL); Handle name = args.at(2); Handle value = args.at(3); HandleScope scope(isolate); LOG(isolate, ApiNamedPropertyAccess("store", recv, *name)); CustomArguments custom_args(isolate, callback->data(), recv, recv); v8::AccessorInfo info(custom_args.end()); { // Leaving JavaScript. VMState state(isolate, EXTERNAL); ExternalCallbackScope call_scope(isolate, setter_address); fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); return *value; } static const int kAccessorInfoOffsetInInterceptorArgs = 2; /** * 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) { Handle name_handle = args.at(0); Handle interceptor_info = args.at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); ASSERT(args[2]->IsJSObject()); // Receiver. ASSERT(args[3]->IsJSObject()); // Holder. ASSERT(args.length() == 5); // Last arg is data object. Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args.arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { return *v8::Utils::OpenHandle(*r); } } return isolate->heap()->no_interceptor_result_sentinel(); } static MaybeObject* ThrowReferenceError(String* 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. IC ic(IC::NO_EXTRA_FRAME, Isolate::Current()); ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub()); if (!ic.SlowIsContextual()) return HEAP->undefined_value(); // Throw a reference error. HandleScope scope; Handle name_handle(name); Handle error = FACTORY->NewReferenceError("not_defined", HandleVector(&name_handle, 1)); return Isolate::Current()->Throw(*error); } static MaybeObject* LoadWithInterceptor(Arguments* args, PropertyAttributes* attrs) { Handle name_handle = args->at(0); Handle interceptor_info = args->at(1); ASSERT(kAccessorInfoOffsetInInterceptorArgs == 2); Handle receiver_handle = args->at(2); Handle holder_handle = args->at(3); ASSERT(args->length() == 5); // Last arg is data object. Isolate* isolate = receiver_handle->GetIsolate(); Address getter_address = v8::ToCData(interceptor_info->getter()); v8::NamedPropertyGetter getter = FUNCTION_CAST(getter_address); ASSERT(getter != NULL); { // Use the interceptor getter. v8::AccessorInfo info(args->arguments() - kAccessorInfoOffsetInInterceptorArgs); HandleScope scope(isolate); v8::Handle r; { // Leaving JavaScript. VMState state(isolate, EXTERNAL); r = getter(v8::Utils::ToLocal(name_handle), info); } RETURN_IF_SCHEDULED_EXCEPTION(isolate); if (!r.IsEmpty()) { *attrs = NONE; return *v8::Utils::OpenHandle(*r); } } MaybeObject* result = holder_handle->GetPropertyPostInterceptor( *receiver_handle, *name_handle, attrs); RETURN_IF_SCHEDULED_EXCEPTION(isolate); return result; } /** * Loads a property with an interceptor performing post interceptor * lookup if interceptor failed. */ RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForLoad) { PropertyAttributes attr = NONE; Object* result; { MaybeObject* maybe_result = LoadWithInterceptor(&args, &attr); if (!maybe_result->ToObject(&result)) return maybe_result; } // If the property is present, return it. if (attr != ABSENT) return result; return ThrowReferenceError(String::cast(args[0])); } RUNTIME_FUNCTION(MaybeObject*, LoadPropertyWithInterceptorForCall) { PropertyAttributes attr; MaybeObject* result = LoadWithInterceptor(&args, &attr); RETURN_IF_SCHEDULED_EXCEPTION(isolate); // 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) { ASSERT(args.length() == 4); JSObject* recv = JSObject::cast(args[0]); String* name = String::cast(args[1]); Object* value = args[2]; ASSERT(args.smi_at(3) == kStrictMode || args.smi_at(3) == kNonStrictMode); StrictModeFlag strict_mode = static_cast(args.smi_at(3)); ASSERT(recv->HasNamedInterceptor()); PropertyAttributes attr = NONE; MaybeObject* result = recv->SetPropertyWithInterceptor( name, value, attr, strict_mode); 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); Code::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); Code::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::CompileCallInitialize(Code::Flags flags) { int argc = Code::ExtractArgumentsCountFromFlags(flags); Code::Kind kind = Code::ExtractKindFromFlags(flags); Code::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); Code::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
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); Code::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::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); Code::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
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