OLD | NEW |
| (Empty) |
1 // Copyright 2012 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "src/v8.h" | |
6 | |
7 #include "src/api.h" | |
8 #include "src/arguments.h" | |
9 #include "src/ast.h" | |
10 #include "src/code-stubs.h" | |
11 #include "src/cpu-profiler.h" | |
12 #include "src/gdb-jit.h" | |
13 #include "src/ic-inl.h" | |
14 #include "src/stub-cache.h" | |
15 #include "src/type-info.h" | |
16 #include "src/vm-state-inl.h" | |
17 | |
18 namespace v8 { | |
19 namespace internal { | |
20 | |
21 // ----------------------------------------------------------------------- | |
22 // StubCache implementation. | |
23 | |
24 | |
25 StubCache::StubCache(Isolate* isolate) | |
26 : isolate_(isolate) { } | |
27 | |
28 | |
29 void StubCache::Initialize() { | |
30 DCHECK(IsPowerOf2(kPrimaryTableSize)); | |
31 DCHECK(IsPowerOf2(kSecondaryTableSize)); | |
32 Clear(); | |
33 } | |
34 | |
35 | |
36 static Code::Flags CommonStubCacheChecks(Name* name, Map* map, | |
37 Code::Flags flags) { | |
38 flags = Code::RemoveTypeAndHolderFromFlags(flags); | |
39 | |
40 // Validate that the name does not move on scavenge, and that we | |
41 // can use identity checks instead of structural equality checks. | |
42 DCHECK(!name->GetHeap()->InNewSpace(name)); | |
43 DCHECK(name->IsUniqueName()); | |
44 | |
45 // The state bits are not important to the hash function because the stub | |
46 // cache only contains handlers. Make sure that the bits are the least | |
47 // significant so they will be the ones masked out. | |
48 DCHECK_EQ(Code::HANDLER, Code::ExtractKindFromFlags(flags)); | |
49 STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1); | |
50 | |
51 // Make sure that the code type and cache holder are not included in the hash. | |
52 DCHECK(Code::ExtractTypeFromFlags(flags) == 0); | |
53 DCHECK(Code::ExtractCacheHolderFromFlags(flags) == 0); | |
54 | |
55 return flags; | |
56 } | |
57 | |
58 | |
59 Code* StubCache::Set(Name* name, Map* map, Code* code) { | |
60 Code::Flags flags = CommonStubCacheChecks(name, map, code->flags()); | |
61 | |
62 // Compute the primary entry. | |
63 int primary_offset = PrimaryOffset(name, flags, map); | |
64 Entry* primary = entry(primary_, primary_offset); | |
65 Code* old_code = primary->value; | |
66 | |
67 // If the primary entry has useful data in it, we retire it to the | |
68 // secondary cache before overwriting it. | |
69 if (old_code != isolate_->builtins()->builtin(Builtins::kIllegal)) { | |
70 Map* old_map = primary->map; | |
71 Code::Flags old_flags = | |
72 Code::RemoveTypeAndHolderFromFlags(old_code->flags()); | |
73 int seed = PrimaryOffset(primary->key, old_flags, old_map); | |
74 int secondary_offset = SecondaryOffset(primary->key, old_flags, seed); | |
75 Entry* secondary = entry(secondary_, secondary_offset); | |
76 *secondary = *primary; | |
77 } | |
78 | |
79 // Update primary cache. | |
80 primary->key = name; | |
81 primary->value = code; | |
82 primary->map = map; | |
83 isolate()->counters()->megamorphic_stub_cache_updates()->Increment(); | |
84 return code; | |
85 } | |
86 | |
87 | |
88 Code* StubCache::Get(Name* name, Map* map, Code::Flags flags) { | |
89 flags = CommonStubCacheChecks(name, map, flags); | |
90 int primary_offset = PrimaryOffset(name, flags, map); | |
91 Entry* primary = entry(primary_, primary_offset); | |
92 if (primary->key == name && primary->map == map) { | |
93 return primary->value; | |
94 } | |
95 int secondary_offset = SecondaryOffset(name, flags, primary_offset); | |
96 Entry* secondary = entry(secondary_, secondary_offset); | |
97 if (secondary->key == name && secondary->map == map) { | |
98 return secondary->value; | |
99 } | |
100 return NULL; | |
101 } | |
102 | |
103 | |
104 Handle<Code> PropertyICCompiler::Find(Handle<Name> name, | |
105 Handle<Map> stub_holder, Code::Kind kind, | |
106 ExtraICState extra_state, | |
107 CacheHolderFlag cache_holder) { | |
108 Code::Flags flags = Code::ComputeMonomorphicFlags( | |
109 kind, extra_state, cache_holder); | |
110 Object* probe = stub_holder->FindInCodeCache(*name, flags); | |
111 if (probe->IsCode()) return handle(Code::cast(probe)); | |
112 return Handle<Code>::null(); | |
113 } | |
114 | |
115 | |
116 Handle<Code> PropertyHandlerCompiler::Find(Handle<Name> name, | |
117 Handle<Map> stub_holder, | |
118 Code::Kind kind, | |
119 CacheHolderFlag cache_holder, | |
120 Code::StubType type) { | |
121 Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder); | |
122 Object* probe = stub_holder->FindInCodeCache(*name, flags); | |
123 if (probe->IsCode()) return handle(Code::cast(probe)); | |
124 return Handle<Code>::null(); | |
125 } | |
126 | |
127 | |
128 Handle<Code> PropertyICCompiler::ComputeMonomorphic( | |
129 Code::Kind kind, Handle<Name> name, Handle<HeapType> type, | |
130 Handle<Code> handler, ExtraICState extra_ic_state) { | |
131 Isolate* isolate = name->GetIsolate(); | |
132 if (handler.is_identical_to(isolate->builtins()->LoadIC_Normal()) || | |
133 handler.is_identical_to(isolate->builtins()->StoreIC_Normal())) { | |
134 name = isolate->factory()->normal_ic_symbol(); | |
135 } | |
136 | |
137 CacheHolderFlag flag; | |
138 Handle<Map> stub_holder = IC::GetICCacheHolder(*type, isolate, &flag); | |
139 | |
140 Handle<Code> ic; | |
141 // There are multiple string maps that all use the same prototype. That | |
142 // prototype cannot hold multiple handlers, one for each of the string maps, | |
143 // for a single name. Hence, turn off caching of the IC. | |
144 bool can_be_cached = !type->Is(HeapType::String()); | |
145 if (can_be_cached) { | |
146 ic = Find(name, stub_holder, kind, extra_ic_state, flag); | |
147 if (!ic.is_null()) return ic; | |
148 } | |
149 | |
150 #ifdef DEBUG | |
151 if (kind == Code::KEYED_STORE_IC) { | |
152 DCHECK(STANDARD_STORE == | |
153 KeyedStoreIC::GetKeyedAccessStoreMode(extra_ic_state)); | |
154 } | |
155 #endif | |
156 | |
157 PropertyICCompiler ic_compiler(isolate, kind, extra_ic_state, flag); | |
158 ic = ic_compiler.CompileMonomorphic(type, handler, name, PROPERTY); | |
159 | |
160 if (can_be_cached) Map::UpdateCodeCache(stub_holder, name, ic); | |
161 return ic; | |
162 } | |
163 | |
164 | |
165 Handle<Code> NamedLoadHandlerCompiler::ComputeLoadNonexistent( | |
166 Handle<Name> name, Handle<HeapType> type) { | |
167 Isolate* isolate = name->GetIsolate(); | |
168 Handle<Map> receiver_map = IC::TypeToMap(*type, isolate); | |
169 if (receiver_map->prototype()->IsNull()) { | |
170 // TODO(jkummerow/verwaest): If there is no prototype and the property | |
171 // is nonexistent, introduce a builtin to handle this (fast properties | |
172 // -> return undefined, dictionary properties -> do negative lookup). | |
173 return Handle<Code>(); | |
174 } | |
175 CacheHolderFlag flag; | |
176 Handle<Map> stub_holder_map = | |
177 IC::GetHandlerCacheHolder(*type, false, isolate, &flag); | |
178 | |
179 // If no dictionary mode objects are present in the prototype chain, the load | |
180 // nonexistent IC stub can be shared for all names for a given map and we use | |
181 // the empty string for the map cache in that case. If there are dictionary | |
182 // mode objects involved, we need to do negative lookups in the stub and | |
183 // therefore the stub will be specific to the name. | |
184 Handle<Name> cache_name = | |
185 receiver_map->is_dictionary_map() | |
186 ? name | |
187 : Handle<Name>::cast(isolate->factory()->nonexistent_symbol()); | |
188 Handle<Map> current_map = stub_holder_map; | |
189 Handle<JSObject> last(JSObject::cast(receiver_map->prototype())); | |
190 while (true) { | |
191 if (current_map->is_dictionary_map()) cache_name = name; | |
192 if (current_map->prototype()->IsNull()) break; | |
193 last = handle(JSObject::cast(current_map->prototype())); | |
194 current_map = handle(last->map()); | |
195 } | |
196 // Compile the stub that is either shared for all names or | |
197 // name specific if there are global objects involved. | |
198 Handle<Code> handler = PropertyHandlerCompiler::Find( | |
199 cache_name, stub_holder_map, Code::LOAD_IC, flag, Code::FAST); | |
200 if (!handler.is_null()) return handler; | |
201 | |
202 NamedLoadHandlerCompiler compiler(isolate, type, last, flag); | |
203 handler = compiler.CompileLoadNonexistent(cache_name); | |
204 Map::UpdateCodeCache(stub_holder_map, cache_name, handler); | |
205 return handler; | |
206 } | |
207 | |
208 | |
209 Handle<Code> PropertyICCompiler::ComputeKeyedLoadMonomorphic( | |
210 Handle<Map> receiver_map) { | |
211 Isolate* isolate = receiver_map->GetIsolate(); | |
212 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC); | |
213 Handle<Name> name = isolate->factory()->KeyedLoadMonomorphic_string(); | |
214 | |
215 Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate); | |
216 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
217 | |
218 ElementsKind elements_kind = receiver_map->elements_kind(); | |
219 Handle<Code> stub; | |
220 if (receiver_map->has_fast_elements() || | |
221 receiver_map->has_external_array_elements() || | |
222 receiver_map->has_fixed_typed_array_elements()) { | |
223 stub = LoadFastElementStub(isolate, | |
224 receiver_map->instance_type() == JS_ARRAY_TYPE, | |
225 elements_kind).GetCode(); | |
226 } else { | |
227 stub = FLAG_compiled_keyed_dictionary_loads | |
228 ? LoadDictionaryElementStub(isolate).GetCode() | |
229 : LoadDictionaryElementPlatformStub(isolate).GetCode(); | |
230 } | |
231 PropertyICCompiler compiler(isolate, Code::KEYED_LOAD_IC); | |
232 Handle<Code> code = | |
233 compiler.CompileMonomorphic(HeapType::Class(receiver_map, isolate), stub, | |
234 isolate->factory()->empty_string(), ELEMENT); | |
235 | |
236 Map::UpdateCodeCache(receiver_map, name, code); | |
237 return code; | |
238 } | |
239 | |
240 | |
241 Handle<Code> PropertyICCompiler::ComputeKeyedStoreMonomorphic( | |
242 Handle<Map> receiver_map, StrictMode strict_mode, | |
243 KeyedAccessStoreMode store_mode) { | |
244 Isolate* isolate = receiver_map->GetIsolate(); | |
245 ExtraICState extra_state = | |
246 KeyedStoreIC::ComputeExtraICState(strict_mode, store_mode); | |
247 Code::Flags flags = | |
248 Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, extra_state); | |
249 | |
250 DCHECK(store_mode == STANDARD_STORE || | |
251 store_mode == STORE_AND_GROW_NO_TRANSITION || | |
252 store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || | |
253 store_mode == STORE_NO_TRANSITION_HANDLE_COW); | |
254 | |
255 Handle<String> name = isolate->factory()->KeyedStoreMonomorphic_string(); | |
256 Handle<Object> probe(receiver_map->FindInCodeCache(*name, flags), isolate); | |
257 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
258 | |
259 PropertyICCompiler compiler(isolate, Code::KEYED_STORE_IC, extra_state); | |
260 Handle<Code> code = | |
261 compiler.CompileKeyedStoreMonomorphic(receiver_map, store_mode); | |
262 | |
263 Map::UpdateCodeCache(receiver_map, name, code); | |
264 DCHECK(KeyedStoreIC::GetKeyedAccessStoreMode(code->extra_ic_state()) | |
265 == store_mode); | |
266 return code; | |
267 } | |
268 | |
269 | |
270 #define CALL_LOGGER_TAG(kind, type) (Logger::KEYED_##type) | |
271 | |
272 static void FillCache(Isolate* isolate, Handle<Code> code) { | |
273 Handle<UnseededNumberDictionary> dictionary = | |
274 UnseededNumberDictionary::Set(isolate->factory()->non_monomorphic_cache(), | |
275 code->flags(), | |
276 code); | |
277 isolate->heap()->public_set_non_monomorphic_cache(*dictionary); | |
278 } | |
279 | |
280 | |
281 Code* PropertyICCompiler::FindPreMonomorphic(Isolate* isolate, Code::Kind kind, | |
282 ExtraICState state) { | |
283 Code::Flags flags = Code::ComputeFlags(kind, PREMONOMORPHIC, state); | |
284 UnseededNumberDictionary* dictionary = | |
285 isolate->heap()->non_monomorphic_cache(); | |
286 int entry = dictionary->FindEntry(isolate, flags); | |
287 DCHECK(entry != -1); | |
288 Object* code = dictionary->ValueAt(entry); | |
289 // This might be called during the marking phase of the collector | |
290 // hence the unchecked cast. | |
291 return reinterpret_cast<Code*>(code); | |
292 } | |
293 | |
294 | |
295 Handle<Code> PropertyICCompiler::ComputeLoad(Isolate* isolate, | |
296 InlineCacheState ic_state, | |
297 ExtraICState extra_state) { | |
298 Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, ic_state, extra_state); | |
299 Handle<UnseededNumberDictionary> cache = | |
300 isolate->factory()->non_monomorphic_cache(); | |
301 int entry = cache->FindEntry(isolate, flags); | |
302 if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); | |
303 | |
304 PropertyICCompiler compiler(isolate, Code::LOAD_IC); | |
305 Handle<Code> code; | |
306 if (ic_state == UNINITIALIZED) { | |
307 code = compiler.CompileLoadInitialize(flags); | |
308 } else if (ic_state == PREMONOMORPHIC) { | |
309 code = compiler.CompileLoadPreMonomorphic(flags); | |
310 } else if (ic_state == MEGAMORPHIC) { | |
311 code = compiler.CompileLoadMegamorphic(flags); | |
312 } else { | |
313 UNREACHABLE(); | |
314 } | |
315 FillCache(isolate, code); | |
316 return code; | |
317 } | |
318 | |
319 | |
320 Handle<Code> PropertyICCompiler::ComputeStore(Isolate* isolate, | |
321 InlineCacheState ic_state, | |
322 ExtraICState extra_state) { | |
323 Code::Flags flags = Code::ComputeFlags(Code::STORE_IC, ic_state, extra_state); | |
324 Handle<UnseededNumberDictionary> cache = | |
325 isolate->factory()->non_monomorphic_cache(); | |
326 int entry = cache->FindEntry(isolate, flags); | |
327 if (entry != -1) return Handle<Code>(Code::cast(cache->ValueAt(entry))); | |
328 | |
329 PropertyICCompiler compiler(isolate, Code::STORE_IC); | |
330 Handle<Code> code; | |
331 if (ic_state == UNINITIALIZED) { | |
332 code = compiler.CompileStoreInitialize(flags); | |
333 } else if (ic_state == PREMONOMORPHIC) { | |
334 code = compiler.CompileStorePreMonomorphic(flags); | |
335 } else if (ic_state == GENERIC) { | |
336 code = compiler.CompileStoreGeneric(flags); | |
337 } else if (ic_state == MEGAMORPHIC) { | |
338 code = compiler.CompileStoreMegamorphic(flags); | |
339 } else { | |
340 UNREACHABLE(); | |
341 } | |
342 | |
343 FillCache(isolate, code); | |
344 return code; | |
345 } | |
346 | |
347 | |
348 Handle<Code> PropertyICCompiler::ComputeCompareNil(Handle<Map> receiver_map, | |
349 CompareNilICStub* stub) { | |
350 Isolate* isolate = receiver_map->GetIsolate(); | |
351 Handle<String> name(isolate->heap()->empty_string()); | |
352 if (!receiver_map->is_dictionary_map()) { | |
353 Handle<Code> cached_ic = | |
354 Find(name, receiver_map, Code::COMPARE_NIL_IC, stub->GetExtraICState()); | |
355 if (!cached_ic.is_null()) return cached_ic; | |
356 } | |
357 | |
358 Code::FindAndReplacePattern pattern; | |
359 pattern.Add(isolate->factory()->meta_map(), receiver_map); | |
360 Handle<Code> ic = stub->GetCodeCopy(pattern); | |
361 | |
362 if (!receiver_map->is_dictionary_map()) { | |
363 Map::UpdateCodeCache(receiver_map, name, ic); | |
364 } | |
365 | |
366 return ic; | |
367 } | |
368 | |
369 | |
370 // TODO(verwaest): Change this method so it takes in a TypeHandleList. | |
371 Handle<Code> PropertyICCompiler::ComputeKeyedLoadPolymorphic( | |
372 MapHandleList* receiver_maps) { | |
373 Isolate* isolate = receiver_maps->at(0)->GetIsolate(); | |
374 Code::Flags flags = Code::ComputeFlags(Code::KEYED_LOAD_IC, POLYMORPHIC); | |
375 Handle<PolymorphicCodeCache> cache = | |
376 isolate->factory()->polymorphic_code_cache(); | |
377 Handle<Object> probe = cache->Lookup(receiver_maps, flags); | |
378 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
379 | |
380 TypeHandleList types(receiver_maps->length()); | |
381 for (int i = 0; i < receiver_maps->length(); i++) { | |
382 types.Add(HeapType::Class(receiver_maps->at(i), isolate)); | |
383 } | |
384 CodeHandleList handlers(receiver_maps->length()); | |
385 ElementHandlerCompiler compiler(isolate); | |
386 compiler.CompileElementHandlers(receiver_maps, &handlers); | |
387 PropertyICCompiler ic_compiler(isolate, Code::KEYED_LOAD_IC); | |
388 Handle<Code> code = ic_compiler.CompilePolymorphic( | |
389 &types, &handlers, isolate->factory()->empty_string(), Code::NORMAL, | |
390 ELEMENT); | |
391 | |
392 isolate->counters()->keyed_load_polymorphic_stubs()->Increment(); | |
393 | |
394 PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); | |
395 return code; | |
396 } | |
397 | |
398 | |
399 Handle<Code> PropertyICCompiler::ComputePolymorphic( | |
400 Code::Kind kind, TypeHandleList* types, CodeHandleList* handlers, | |
401 int valid_types, Handle<Name> name, ExtraICState extra_ic_state) { | |
402 Handle<Code> handler = handlers->at(0); | |
403 Code::StubType type = valid_types == 1 ? handler->type() : Code::NORMAL; | |
404 DCHECK(kind == Code::LOAD_IC || kind == Code::STORE_IC); | |
405 PropertyICCompiler ic_compiler(name->GetIsolate(), kind, extra_ic_state); | |
406 return ic_compiler.CompilePolymorphic(types, handlers, name, type, PROPERTY); | |
407 } | |
408 | |
409 | |
410 Handle<Code> PropertyICCompiler::ComputeKeyedStorePolymorphic( | |
411 MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode, | |
412 StrictMode strict_mode) { | |
413 Isolate* isolate = receiver_maps->at(0)->GetIsolate(); | |
414 DCHECK(store_mode == STANDARD_STORE || | |
415 store_mode == STORE_AND_GROW_NO_TRANSITION || | |
416 store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS || | |
417 store_mode == STORE_NO_TRANSITION_HANDLE_COW); | |
418 Handle<PolymorphicCodeCache> cache = | |
419 isolate->factory()->polymorphic_code_cache(); | |
420 ExtraICState extra_state = KeyedStoreIC::ComputeExtraICState( | |
421 strict_mode, store_mode); | |
422 Code::Flags flags = | |
423 Code::ComputeFlags(Code::KEYED_STORE_IC, POLYMORPHIC, extra_state); | |
424 Handle<Object> probe = cache->Lookup(receiver_maps, flags); | |
425 if (probe->IsCode()) return Handle<Code>::cast(probe); | |
426 | |
427 PropertyICCompiler compiler(isolate, Code::KEYED_STORE_IC, extra_state); | |
428 Handle<Code> code = | |
429 compiler.CompileKeyedStorePolymorphic(receiver_maps, store_mode); | |
430 PolymorphicCodeCache::Update(cache, receiver_maps, flags, code); | |
431 return code; | |
432 } | |
433 | |
434 | |
435 void StubCache::Clear() { | |
436 Code* empty = isolate_->builtins()->builtin(Builtins::kIllegal); | |
437 for (int i = 0; i < kPrimaryTableSize; i++) { | |
438 primary_[i].key = isolate()->heap()->empty_string(); | |
439 primary_[i].map = NULL; | |
440 primary_[i].value = empty; | |
441 } | |
442 for (int j = 0; j < kSecondaryTableSize; j++) { | |
443 secondary_[j].key = isolate()->heap()->empty_string(); | |
444 secondary_[j].map = NULL; | |
445 secondary_[j].value = empty; | |
446 } | |
447 } | |
448 | |
449 | |
450 void StubCache::CollectMatchingMaps(SmallMapList* types, | |
451 Handle<Name> name, | |
452 Code::Flags flags, | |
453 Handle<Context> native_context, | |
454 Zone* zone) { | |
455 for (int i = 0; i < kPrimaryTableSize; i++) { | |
456 if (primary_[i].key == *name) { | |
457 Map* map = primary_[i].map; | |
458 // Map can be NULL, if the stub is constant function call | |
459 // with a primitive receiver. | |
460 if (map == NULL) continue; | |
461 | |
462 int offset = PrimaryOffset(*name, flags, map); | |
463 if (entry(primary_, offset) == &primary_[i] && | |
464 !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { | |
465 types->AddMapIfMissing(Handle<Map>(map), zone); | |
466 } | |
467 } | |
468 } | |
469 | |
470 for (int i = 0; i < kSecondaryTableSize; i++) { | |
471 if (secondary_[i].key == *name) { | |
472 Map* map = secondary_[i].map; | |
473 // Map can be NULL, if the stub is constant function call | |
474 // with a primitive receiver. | |
475 if (map == NULL) continue; | |
476 | |
477 // Lookup in primary table and skip duplicates. | |
478 int primary_offset = PrimaryOffset(*name, flags, map); | |
479 | |
480 // Lookup in secondary table and add matches. | |
481 int offset = SecondaryOffset(*name, flags, primary_offset); | |
482 if (entry(secondary_, offset) == &secondary_[i] && | |
483 !TypeFeedbackOracle::CanRetainOtherContext(map, *native_context)) { | |
484 types->AddMapIfMissing(Handle<Map>(map), zone); | |
485 } | |
486 } | |
487 } | |
488 } | |
489 | |
490 | |
491 // ------------------------------------------------------------------------ | |
492 // StubCompiler implementation. | |
493 | |
494 | |
495 RUNTIME_FUNCTION(StoreCallbackProperty) { | |
496 Handle<JSObject> receiver = args.at<JSObject>(0); | |
497 Handle<JSObject> holder = args.at<JSObject>(1); | |
498 Handle<ExecutableAccessorInfo> callback = args.at<ExecutableAccessorInfo>(2); | |
499 Handle<Name> name = args.at<Name>(3); | |
500 Handle<Object> value = args.at<Object>(4); | |
501 HandleScope scope(isolate); | |
502 | |
503 DCHECK(callback->IsCompatibleReceiver(*receiver)); | |
504 | |
505 Address setter_address = v8::ToCData<Address>(callback->setter()); | |
506 v8::AccessorNameSetterCallback fun = | |
507 FUNCTION_CAST<v8::AccessorNameSetterCallback>(setter_address); | |
508 DCHECK(fun != NULL); | |
509 | |
510 LOG(isolate, ApiNamedPropertyAccess("store", *receiver, *name)); | |
511 PropertyCallbackArguments custom_args(isolate, callback->data(), *receiver, | |
512 *holder); | |
513 custom_args.Call(fun, v8::Utils::ToLocal(name), v8::Utils::ToLocal(value)); | |
514 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); | |
515 return *value; | |
516 } | |
517 | |
518 | |
519 /** | |
520 * Attempts to load a property with an interceptor (which must be present), | |
521 * but doesn't search the prototype chain. | |
522 * | |
523 * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't | |
524 * provide any value for the given name. | |
525 */ | |
526 RUNTIME_FUNCTION(LoadPropertyWithInterceptorOnly) { | |
527 DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength); | |
528 Handle<Name> name_handle = | |
529 args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex); | |
530 Handle<InterceptorInfo> interceptor_info = args.at<InterceptorInfo>( | |
531 NamedLoadHandlerCompiler::kInterceptorArgsInfoIndex); | |
532 | |
533 // TODO(rossberg): Support symbols in the API. | |
534 if (name_handle->IsSymbol()) | |
535 return isolate->heap()->no_interceptor_result_sentinel(); | |
536 Handle<String> name = Handle<String>::cast(name_handle); | |
537 | |
538 Address getter_address = v8::ToCData<Address>(interceptor_info->getter()); | |
539 v8::NamedPropertyGetterCallback getter = | |
540 FUNCTION_CAST<v8::NamedPropertyGetterCallback>(getter_address); | |
541 DCHECK(getter != NULL); | |
542 | |
543 Handle<JSObject> receiver = | |
544 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex); | |
545 Handle<JSObject> holder = | |
546 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex); | |
547 PropertyCallbackArguments callback_args( | |
548 isolate, interceptor_info->data(), *receiver, *holder); | |
549 { | |
550 // Use the interceptor getter. | |
551 HandleScope scope(isolate); | |
552 v8::Handle<v8::Value> r = | |
553 callback_args.Call(getter, v8::Utils::ToLocal(name)); | |
554 RETURN_FAILURE_IF_SCHEDULED_EXCEPTION(isolate); | |
555 if (!r.IsEmpty()) { | |
556 Handle<Object> result = v8::Utils::OpenHandle(*r); | |
557 result->VerifyApiCallResultType(); | |
558 return *v8::Utils::OpenHandle(*r); | |
559 } | |
560 } | |
561 | |
562 return isolate->heap()->no_interceptor_result_sentinel(); | |
563 } | |
564 | |
565 | |
566 static Object* ThrowReferenceError(Isolate* isolate, Name* name) { | |
567 // If the load is non-contextual, just return the undefined result. | |
568 // Note that both keyed and non-keyed loads may end up here. | |
569 HandleScope scope(isolate); | |
570 LoadIC ic(IC::NO_EXTRA_FRAME, isolate); | |
571 if (ic.contextual_mode() != CONTEXTUAL) { | |
572 return isolate->heap()->undefined_value(); | |
573 } | |
574 | |
575 // Throw a reference error. | |
576 Handle<Name> name_handle(name); | |
577 Handle<Object> error = | |
578 isolate->factory()->NewReferenceError("not_defined", | |
579 HandleVector(&name_handle, 1)); | |
580 return isolate->Throw(*error); | |
581 } | |
582 | |
583 | |
584 /** | |
585 * Loads a property with an interceptor performing post interceptor | |
586 * lookup if interceptor failed. | |
587 */ | |
588 RUNTIME_FUNCTION(LoadPropertyWithInterceptor) { | |
589 HandleScope scope(isolate); | |
590 DCHECK(args.length() == NamedLoadHandlerCompiler::kInterceptorArgsLength); | |
591 Handle<Name> name = | |
592 args.at<Name>(NamedLoadHandlerCompiler::kInterceptorArgsNameIndex); | |
593 Handle<JSObject> receiver = | |
594 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsThisIndex); | |
595 Handle<JSObject> holder = | |
596 args.at<JSObject>(NamedLoadHandlerCompiler::kInterceptorArgsHolderIndex); | |
597 | |
598 Handle<Object> result; | |
599 LookupIterator it(receiver, name, holder); | |
600 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
601 isolate, result, JSObject::GetProperty(&it)); | |
602 | |
603 if (it.IsFound()) return *result; | |
604 | |
605 return ThrowReferenceError(isolate, Name::cast(args[0])); | |
606 } | |
607 | |
608 | |
609 RUNTIME_FUNCTION(StorePropertyWithInterceptor) { | |
610 HandleScope scope(isolate); | |
611 DCHECK(args.length() == 3); | |
612 StoreIC ic(IC::NO_EXTRA_FRAME, isolate); | |
613 Handle<JSObject> receiver = args.at<JSObject>(0); | |
614 Handle<Name> name = args.at<Name>(1); | |
615 Handle<Object> value = args.at<Object>(2); | |
616 #ifdef DEBUG | |
617 PrototypeIterator iter(isolate, receiver, | |
618 PrototypeIterator::START_AT_RECEIVER); | |
619 bool found = false; | |
620 while (!iter.IsAtEnd(PrototypeIterator::END_AT_NON_HIDDEN)) { | |
621 Handle<Object> current = PrototypeIterator::GetCurrent(iter); | |
622 if (current->IsJSObject() && | |
623 Handle<JSObject>::cast(current)->HasNamedInterceptor()) { | |
624 found = true; | |
625 break; | |
626 } | |
627 } | |
628 DCHECK(found); | |
629 #endif | |
630 Handle<Object> result; | |
631 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
632 isolate, result, | |
633 JSObject::SetProperty(receiver, name, value, ic.strict_mode())); | |
634 return *result; | |
635 } | |
636 | |
637 | |
638 RUNTIME_FUNCTION(LoadElementWithInterceptor) { | |
639 HandleScope scope(isolate); | |
640 Handle<JSObject> receiver = args.at<JSObject>(0); | |
641 DCHECK(args.smi_at(1) >= 0); | |
642 uint32_t index = args.smi_at(1); | |
643 Handle<Object> result; | |
644 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
645 isolate, result, | |
646 JSObject::GetElementWithInterceptor(receiver, receiver, index)); | |
647 return *result; | |
648 } | |
649 | |
650 | |
651 Handle<Code> PropertyICCompiler::CompileLoadInitialize(Code::Flags flags) { | |
652 LoadIC::GenerateInitialize(masm()); | |
653 Handle<Code> code = GetCodeWithFlags(flags, "CompileLoadInitialize"); | |
654 PROFILE(isolate(), | |
655 CodeCreateEvent(Logger::LOAD_INITIALIZE_TAG, *code, 0)); | |
656 return code; | |
657 } | |
658 | |
659 | |
660 Handle<Code> PropertyICCompiler::CompileLoadPreMonomorphic(Code::Flags flags) { | |
661 LoadIC::GeneratePreMonomorphic(masm()); | |
662 Handle<Code> code = GetCodeWithFlags(flags, "CompileLoadPreMonomorphic"); | |
663 PROFILE(isolate(), | |
664 CodeCreateEvent(Logger::LOAD_PREMONOMORPHIC_TAG, *code, 0)); | |
665 return code; | |
666 } | |
667 | |
668 | |
669 Handle<Code> PropertyICCompiler::CompileLoadMegamorphic(Code::Flags flags) { | |
670 LoadIC::GenerateMegamorphic(masm()); | |
671 Handle<Code> code = GetCodeWithFlags(flags, "CompileLoadMegamorphic"); | |
672 PROFILE(isolate(), | |
673 CodeCreateEvent(Logger::LOAD_MEGAMORPHIC_TAG, *code, 0)); | |
674 return code; | |
675 } | |
676 | |
677 | |
678 Handle<Code> PropertyICCompiler::CompileStoreInitialize(Code::Flags flags) { | |
679 StoreIC::GenerateInitialize(masm()); | |
680 Handle<Code> code = GetCodeWithFlags(flags, "CompileStoreInitialize"); | |
681 PROFILE(isolate(), | |
682 CodeCreateEvent(Logger::STORE_INITIALIZE_TAG, *code, 0)); | |
683 return code; | |
684 } | |
685 | |
686 | |
687 Handle<Code> PropertyICCompiler::CompileStorePreMonomorphic(Code::Flags flags) { | |
688 StoreIC::GeneratePreMonomorphic(masm()); | |
689 Handle<Code> code = GetCodeWithFlags(flags, "CompileStorePreMonomorphic"); | |
690 PROFILE(isolate(), | |
691 CodeCreateEvent(Logger::STORE_PREMONOMORPHIC_TAG, *code, 0)); | |
692 return code; | |
693 } | |
694 | |
695 | |
696 Handle<Code> PropertyICCompiler::CompileStoreGeneric(Code::Flags flags) { | |
697 ExtraICState extra_state = Code::ExtractExtraICStateFromFlags(flags); | |
698 StrictMode strict_mode = StoreIC::GetStrictMode(extra_state); | |
699 StoreIC::GenerateRuntimeSetProperty(masm(), strict_mode); | |
700 Handle<Code> code = GetCodeWithFlags(flags, "CompileStoreGeneric"); | |
701 PROFILE(isolate(), | |
702 CodeCreateEvent(Logger::STORE_GENERIC_TAG, *code, 0)); | |
703 return code; | |
704 } | |
705 | |
706 | |
707 Handle<Code> PropertyICCompiler::CompileStoreMegamorphic(Code::Flags flags) { | |
708 StoreIC::GenerateMegamorphic(masm()); | |
709 Handle<Code> code = GetCodeWithFlags(flags, "CompileStoreMegamorphic"); | |
710 PROFILE(isolate(), | |
711 CodeCreateEvent(Logger::STORE_MEGAMORPHIC_TAG, *code, 0)); | |
712 return code; | |
713 } | |
714 | |
715 | |
716 #undef CALL_LOGGER_TAG | |
717 | |
718 | |
719 Handle<Code> PropertyAccessCompiler::GetCodeWithFlags(Code::Flags flags, | |
720 const char* name) { | |
721 // Create code object in the heap. | |
722 CodeDesc desc; | |
723 masm()->GetCode(&desc); | |
724 Handle<Code> code = factory()->NewCode(desc, flags, masm()->CodeObject()); | |
725 if (code->IsCodeStubOrIC()) code->set_stub_key(CodeStub::NoCacheKey()); | |
726 #ifdef ENABLE_DISASSEMBLER | |
727 if (FLAG_print_code_stubs) { | |
728 OFStream os(stdout); | |
729 code->Disassemble(name, os); | |
730 } | |
731 #endif | |
732 return code; | |
733 } | |
734 | |
735 | |
736 Handle<Code> PropertyAccessCompiler::GetCodeWithFlags(Code::Flags flags, | |
737 Handle<Name> name) { | |
738 return (FLAG_print_code_stubs && !name.is_null() && name->IsString()) | |
739 ? GetCodeWithFlags(flags, Handle<String>::cast(name)->ToCString().get()) | |
740 : GetCodeWithFlags(flags, NULL); | |
741 } | |
742 | |
743 | |
744 #define __ ACCESS_MASM(masm()) | |
745 | |
746 | |
747 Register NamedLoadHandlerCompiler::FrontendHeader(Register object_reg, | |
748 Handle<Name> name, | |
749 Label* miss) { | |
750 PrototypeCheckType check_type = CHECK_ALL_MAPS; | |
751 int function_index = -1; | |
752 if (type()->Is(HeapType::String())) { | |
753 function_index = Context::STRING_FUNCTION_INDEX; | |
754 } else if (type()->Is(HeapType::Symbol())) { | |
755 function_index = Context::SYMBOL_FUNCTION_INDEX; | |
756 } else if (type()->Is(HeapType::Number())) { | |
757 function_index = Context::NUMBER_FUNCTION_INDEX; | |
758 } else if (type()->Is(HeapType::Boolean())) { | |
759 function_index = Context::BOOLEAN_FUNCTION_INDEX; | |
760 } else { | |
761 check_type = SKIP_RECEIVER; | |
762 } | |
763 | |
764 if (check_type == CHECK_ALL_MAPS) { | |
765 GenerateDirectLoadGlobalFunctionPrototype( | |
766 masm(), function_index, scratch1(), miss); | |
767 Object* function = isolate()->native_context()->get(function_index); | |
768 Object* prototype = JSFunction::cast(function)->instance_prototype(); | |
769 set_type_for_object(handle(prototype, isolate())); | |
770 object_reg = scratch1(); | |
771 } | |
772 | |
773 // Check that the maps starting from the prototype haven't changed. | |
774 return CheckPrototypes(object_reg, scratch1(), scratch2(), scratch3(), name, | |
775 miss, check_type); | |
776 } | |
777 | |
778 | |
779 // Frontend for store uses the name register. It has to be restored before a | |
780 // miss. | |
781 Register NamedStoreHandlerCompiler::FrontendHeader(Register object_reg, | |
782 Handle<Name> name, | |
783 Label* miss) { | |
784 return CheckPrototypes(object_reg, this->name(), scratch1(), scratch2(), name, | |
785 miss, SKIP_RECEIVER); | |
786 } | |
787 | |
788 | |
789 bool PropertyICCompiler::IncludesNumberType(TypeHandleList* types) { | |
790 for (int i = 0; i < types->length(); ++i) { | |
791 if (types->at(i)->Is(HeapType::Number())) return true; | |
792 } | |
793 return false; | |
794 } | |
795 | |
796 | |
797 Register PropertyHandlerCompiler::Frontend(Register object_reg, | |
798 Handle<Name> name) { | |
799 Label miss; | |
800 Register reg = FrontendHeader(object_reg, name, &miss); | |
801 FrontendFooter(name, &miss); | |
802 return reg; | |
803 } | |
804 | |
805 | |
806 void PropertyHandlerCompiler::NonexistentFrontendHeader(Handle<Name> name, | |
807 Label* miss, | |
808 Register scratch1, | |
809 Register scratch2) { | |
810 Register holder_reg; | |
811 Handle<Map> last_map; | |
812 if (holder().is_null()) { | |
813 holder_reg = receiver(); | |
814 last_map = IC::TypeToMap(*type(), isolate()); | |
815 // If |type| has null as its prototype, |holder()| is | |
816 // Handle<JSObject>::null(). | |
817 DCHECK(last_map->prototype() == isolate()->heap()->null_value()); | |
818 } else { | |
819 holder_reg = FrontendHeader(receiver(), name, miss); | |
820 last_map = handle(holder()->map()); | |
821 } | |
822 | |
823 if (last_map->is_dictionary_map()) { | |
824 if (last_map->IsJSGlobalObjectMap()) { | |
825 Handle<JSGlobalObject> global = | |
826 holder().is_null() | |
827 ? Handle<JSGlobalObject>::cast(type()->AsConstant()->Value()) | |
828 : Handle<JSGlobalObject>::cast(holder()); | |
829 GenerateCheckPropertyCell(masm(), global, name, scratch1, miss); | |
830 } else { | |
831 if (!name->IsUniqueName()) { | |
832 DCHECK(name->IsString()); | |
833 name = factory()->InternalizeString(Handle<String>::cast(name)); | |
834 } | |
835 DCHECK(holder().is_null() || | |
836 holder()->property_dictionary()->FindEntry(name) == | |
837 NameDictionary::kNotFound); | |
838 GenerateDictionaryNegativeLookup(masm(), miss, holder_reg, name, scratch1, | |
839 scratch2); | |
840 } | |
841 } | |
842 } | |
843 | |
844 | |
845 Handle<Code> NamedLoadHandlerCompiler::CompileLoadField(Handle<Name> name, | |
846 FieldIndex field) { | |
847 Register reg = Frontend(receiver(), name); | |
848 __ Move(receiver(), reg); | |
849 LoadFieldStub stub(isolate(), field); | |
850 GenerateTailCall(masm(), stub.GetCode()); | |
851 return GetCode(kind(), Code::FAST, name); | |
852 } | |
853 | |
854 | |
855 Handle<Code> NamedLoadHandlerCompiler::CompileLoadConstant(Handle<Name> name, | |
856 int constant_index) { | |
857 Register reg = Frontend(receiver(), name); | |
858 __ Move(receiver(), reg); | |
859 LoadConstantStub stub(isolate(), constant_index); | |
860 GenerateTailCall(masm(), stub.GetCode()); | |
861 return GetCode(kind(), Code::FAST, name); | |
862 } | |
863 | |
864 | |
865 Handle<Code> NamedLoadHandlerCompiler::CompileLoadNonexistent( | |
866 Handle<Name> name) { | |
867 Label miss; | |
868 NonexistentFrontendHeader(name, &miss, scratch2(), scratch3()); | |
869 GenerateLoadConstant(isolate()->factory()->undefined_value()); | |
870 FrontendFooter(name, &miss); | |
871 return GetCode(kind(), Code::FAST, name); | |
872 } | |
873 | |
874 | |
875 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( | |
876 Handle<Name> name, Handle<ExecutableAccessorInfo> callback) { | |
877 Register reg = Frontend(receiver(), name); | |
878 GenerateLoadCallback(reg, callback); | |
879 return GetCode(kind(), Code::FAST, name); | |
880 } | |
881 | |
882 | |
883 Handle<Code> NamedLoadHandlerCompiler::CompileLoadCallback( | |
884 Handle<Name> name, const CallOptimization& call_optimization) { | |
885 DCHECK(call_optimization.is_simple_api_call()); | |
886 Frontend(receiver(), name); | |
887 Handle<Map> receiver_map = IC::TypeToMap(*type(), isolate()); | |
888 GenerateFastApiCall( | |
889 masm(), call_optimization, receiver_map, | |
890 receiver(), scratch1(), false, 0, NULL); | |
891 return GetCode(kind(), Code::FAST, name); | |
892 } | |
893 | |
894 | |
895 Handle<Code> NamedLoadHandlerCompiler::CompileLoadInterceptor( | |
896 LookupIterator* it) { | |
897 // So far the most popular follow ups for interceptor loads are FIELD and | |
898 // ExecutableAccessorInfo, so inline only them. Other cases may be added | |
899 // later. | |
900 bool inline_followup = it->state() == LookupIterator::PROPERTY; | |
901 if (inline_followup) { | |
902 switch (it->property_kind()) { | |
903 case LookupIterator::DATA: | |
904 inline_followup = it->property_details().type() == FIELD; | |
905 break; | |
906 case LookupIterator::ACCESSOR: { | |
907 Handle<Object> accessors = it->GetAccessors(); | |
908 inline_followup = accessors->IsExecutableAccessorInfo(); | |
909 if (!inline_followup) break; | |
910 Handle<ExecutableAccessorInfo> info = | |
911 Handle<ExecutableAccessorInfo>::cast(accessors); | |
912 inline_followup = info->getter() != NULL && | |
913 ExecutableAccessorInfo::IsCompatibleReceiverType( | |
914 isolate(), info, type()); | |
915 } | |
916 } | |
917 } | |
918 | |
919 Register reg = Frontend(receiver(), it->name()); | |
920 if (inline_followup) { | |
921 // TODO(368): Compile in the whole chain: all the interceptors in | |
922 // prototypes and ultimate answer. | |
923 GenerateLoadInterceptorWithFollowup(it, reg); | |
924 } else { | |
925 GenerateLoadInterceptor(reg); | |
926 } | |
927 return GetCode(kind(), Code::FAST, it->name()); | |
928 } | |
929 | |
930 | |
931 void NamedLoadHandlerCompiler::GenerateLoadPostInterceptor( | |
932 LookupIterator* it, Register interceptor_reg) { | |
933 Handle<JSObject> real_named_property_holder(it->GetHolder<JSObject>()); | |
934 | |
935 set_type_for_object(holder()); | |
936 set_holder(real_named_property_holder); | |
937 Register reg = Frontend(interceptor_reg, it->name()); | |
938 | |
939 switch (it->property_kind()) { | |
940 case LookupIterator::DATA: { | |
941 DCHECK_EQ(FIELD, it->property_details().type()); | |
942 __ Move(receiver(), reg); | |
943 LoadFieldStub stub(isolate(), it->GetFieldIndex()); | |
944 GenerateTailCall(masm(), stub.GetCode()); | |
945 break; | |
946 } | |
947 case LookupIterator::ACCESSOR: | |
948 Handle<ExecutableAccessorInfo> info = | |
949 Handle<ExecutableAccessorInfo>::cast(it->GetAccessors()); | |
950 DCHECK_NE(NULL, info->getter()); | |
951 GenerateLoadCallback(reg, info); | |
952 } | |
953 } | |
954 | |
955 | |
956 Handle<Code> PropertyICCompiler::CompileMonomorphic(Handle<HeapType> type, | |
957 Handle<Code> handler, | |
958 Handle<Name> name, | |
959 IcCheckType check) { | |
960 TypeHandleList types(1); | |
961 CodeHandleList handlers(1); | |
962 types.Add(type); | |
963 handlers.Add(handler); | |
964 Code::StubType stub_type = handler->type(); | |
965 return CompilePolymorphic(&types, &handlers, name, stub_type, check); | |
966 } | |
967 | |
968 | |
969 Handle<Code> NamedLoadHandlerCompiler::CompileLoadViaGetter( | |
970 Handle<Name> name, Handle<JSFunction> getter) { | |
971 Frontend(receiver(), name); | |
972 GenerateLoadViaGetter(masm(), type(), receiver(), getter); | |
973 return GetCode(kind(), Code::FAST, name); | |
974 } | |
975 | |
976 | |
977 // TODO(verwaest): Cleanup. holder() is actually the receiver. | |
978 Handle<Code> NamedStoreHandlerCompiler::CompileStoreTransition( | |
979 Handle<Map> transition, Handle<Name> name) { | |
980 Label miss, slow; | |
981 | |
982 // Ensure no transitions to deprecated maps are followed. | |
983 __ CheckMapDeprecated(transition, scratch1(), &miss); | |
984 | |
985 // Check that we are allowed to write this. | |
986 bool is_nonexistent = holder()->map() == transition->GetBackPointer(); | |
987 if (is_nonexistent) { | |
988 // Find the top object. | |
989 Handle<JSObject> last; | |
990 PrototypeIterator iter(isolate(), holder()); | |
991 while (!iter.IsAtEnd()) { | |
992 last = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); | |
993 iter.Advance(); | |
994 } | |
995 if (!last.is_null()) set_holder(last); | |
996 NonexistentFrontendHeader(name, &miss, scratch1(), scratch2()); | |
997 } else { | |
998 FrontendHeader(receiver(), name, &miss); | |
999 DCHECK(holder()->HasFastProperties()); | |
1000 } | |
1001 | |
1002 GenerateStoreTransition(transition, name, receiver(), this->name(), value(), | |
1003 scratch1(), scratch2(), scratch3(), &miss, &slow); | |
1004 | |
1005 GenerateRestoreName(&miss, name); | |
1006 TailCallBuiltin(masm(), MissBuiltin(kind())); | |
1007 | |
1008 GenerateRestoreName(&slow, name); | |
1009 TailCallBuiltin(masm(), SlowBuiltin(kind())); | |
1010 return GetCode(kind(), Code::FAST, name); | |
1011 } | |
1012 | |
1013 | |
1014 Handle<Code> NamedStoreHandlerCompiler::CompileStoreField(LookupIterator* it) { | |
1015 Label miss; | |
1016 GenerateStoreField(it, value(), &miss); | |
1017 __ bind(&miss); | |
1018 TailCallBuiltin(masm(), MissBuiltin(kind())); | |
1019 return GetCode(kind(), Code::FAST, it->name()); | |
1020 } | |
1021 | |
1022 | |
1023 Handle<Code> NamedStoreHandlerCompiler::CompileStoreViaSetter( | |
1024 Handle<JSObject> object, Handle<Name> name, Handle<JSFunction> setter) { | |
1025 Frontend(receiver(), name); | |
1026 GenerateStoreViaSetter(masm(), type(), receiver(), setter); | |
1027 | |
1028 return GetCode(kind(), Code::FAST, name); | |
1029 } | |
1030 | |
1031 | |
1032 Handle<Code> NamedStoreHandlerCompiler::CompileStoreCallback( | |
1033 Handle<JSObject> object, Handle<Name> name, | |
1034 const CallOptimization& call_optimization) { | |
1035 Frontend(receiver(), name); | |
1036 Register values[] = { value() }; | |
1037 GenerateFastApiCall( | |
1038 masm(), call_optimization, handle(object->map()), | |
1039 receiver(), scratch1(), true, 1, values); | |
1040 return GetCode(kind(), Code::FAST, name); | |
1041 } | |
1042 | |
1043 | |
1044 Handle<Code> PropertyICCompiler::CompileKeyedStoreMonomorphic( | |
1045 Handle<Map> receiver_map, KeyedAccessStoreMode store_mode) { | |
1046 ElementsKind elements_kind = receiver_map->elements_kind(); | |
1047 bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE; | |
1048 Handle<Code> stub; | |
1049 if (receiver_map->has_fast_elements() || | |
1050 receiver_map->has_external_array_elements() || | |
1051 receiver_map->has_fixed_typed_array_elements()) { | |
1052 stub = StoreFastElementStub(isolate(), is_jsarray, elements_kind, | |
1053 store_mode).GetCode(); | |
1054 } else { | |
1055 stub = StoreElementStub(isolate(), is_jsarray, elements_kind, store_mode) | |
1056 .GetCode(); | |
1057 } | |
1058 | |
1059 __ DispatchMap(receiver(), scratch1(), receiver_map, stub, DO_SMI_CHECK); | |
1060 | |
1061 TailCallBuiltin(masm(), Builtins::kKeyedStoreIC_Miss); | |
1062 | |
1063 return GetCode(kind(), Code::NORMAL, factory()->empty_string()); | |
1064 } | |
1065 | |
1066 | |
1067 #undef __ | |
1068 | |
1069 | |
1070 void PropertyAccessCompiler::TailCallBuiltin(MacroAssembler* masm, | |
1071 Builtins::Name name) { | |
1072 Handle<Code> code(masm->isolate()->builtins()->builtin(name)); | |
1073 GenerateTailCall(masm, code); | |
1074 } | |
1075 | |
1076 | |
1077 Register* PropertyAccessCompiler::GetCallingConvention(Code::Kind kind) { | |
1078 if (kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC) { | |
1079 return load_calling_convention(); | |
1080 } | |
1081 DCHECK(kind == Code::STORE_IC || kind == Code::KEYED_STORE_IC); | |
1082 return store_calling_convention(); | |
1083 } | |
1084 | |
1085 | |
1086 Handle<Code> PropertyICCompiler::GetCode(Code::Kind kind, Code::StubType type, | |
1087 Handle<Name> name, | |
1088 InlineCacheState state) { | |
1089 Code::Flags flags = | |
1090 Code::ComputeFlags(kind, state, extra_ic_state_, type, cache_holder()); | |
1091 Handle<Code> code = GetCodeWithFlags(flags, name); | |
1092 IC::RegisterWeakMapDependency(code); | |
1093 PROFILE(isolate(), CodeCreateEvent(log_kind(code), *code, *name)); | |
1094 return code; | |
1095 } | |
1096 | |
1097 | |
1098 Handle<Code> PropertyHandlerCompiler::GetCode(Code::Kind kind, | |
1099 Code::StubType type, | |
1100 Handle<Name> name) { | |
1101 Code::Flags flags = Code::ComputeHandlerFlags(kind, type, cache_holder()); | |
1102 Handle<Code> code = GetCodeWithFlags(flags, name); | |
1103 PROFILE(isolate(), CodeCreateEvent(Logger::STUB_TAG, *code, *name)); | |
1104 return code; | |
1105 } | |
1106 | |
1107 | |
1108 void ElementHandlerCompiler::CompileElementHandlers( | |
1109 MapHandleList* receiver_maps, CodeHandleList* handlers) { | |
1110 for (int i = 0; i < receiver_maps->length(); ++i) { | |
1111 Handle<Map> receiver_map = receiver_maps->at(i); | |
1112 Handle<Code> cached_stub; | |
1113 | |
1114 if ((receiver_map->instance_type() & kNotStringTag) == 0) { | |
1115 cached_stub = isolate()->builtins()->KeyedLoadIC_String(); | |
1116 } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) { | |
1117 cached_stub = isolate()->builtins()->KeyedLoadIC_Slow(); | |
1118 } else { | |
1119 bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; | |
1120 ElementsKind elements_kind = receiver_map->elements_kind(); | |
1121 | |
1122 if (IsFastElementsKind(elements_kind) || | |
1123 IsExternalArrayElementsKind(elements_kind) || | |
1124 IsFixedTypedArrayElementsKind(elements_kind)) { | |
1125 cached_stub = LoadFastElementStub(isolate(), is_js_array, elements_kind) | |
1126 .GetCode(); | |
1127 } else if (elements_kind == SLOPPY_ARGUMENTS_ELEMENTS) { | |
1128 cached_stub = isolate()->builtins()->KeyedLoadIC_SloppyArguments(); | |
1129 } else { | |
1130 DCHECK(elements_kind == DICTIONARY_ELEMENTS); | |
1131 cached_stub = LoadDictionaryElementStub(isolate()).GetCode(); | |
1132 } | |
1133 } | |
1134 | |
1135 handlers->Add(cached_stub); | |
1136 } | |
1137 } | |
1138 | |
1139 | |
1140 Handle<Code> PropertyICCompiler::CompileKeyedStorePolymorphic( | |
1141 MapHandleList* receiver_maps, KeyedAccessStoreMode store_mode) { | |
1142 // Collect MONOMORPHIC stubs for all |receiver_maps|. | |
1143 CodeHandleList handlers(receiver_maps->length()); | |
1144 MapHandleList transitioned_maps(receiver_maps->length()); | |
1145 for (int i = 0; i < receiver_maps->length(); ++i) { | |
1146 Handle<Map> receiver_map(receiver_maps->at(i)); | |
1147 Handle<Code> cached_stub; | |
1148 Handle<Map> transitioned_map = | |
1149 receiver_map->FindTransitionedMap(receiver_maps); | |
1150 | |
1151 // TODO(mvstanton): The code below is doing pessimistic elements | |
1152 // transitions. I would like to stop doing that and rely on Allocation Site | |
1153 // Tracking to do a better job of ensuring the data types are what they need | |
1154 // to be. Not all the elements are in place yet, pessimistic elements | |
1155 // transitions are still important for performance. | |
1156 bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; | |
1157 ElementsKind elements_kind = receiver_map->elements_kind(); | |
1158 if (!transitioned_map.is_null()) { | |
1159 cached_stub = | |
1160 ElementsTransitionAndStoreStub(isolate(), elements_kind, | |
1161 transitioned_map->elements_kind(), | |
1162 is_js_array, store_mode).GetCode(); | |
1163 } else if (receiver_map->instance_type() < FIRST_JS_RECEIVER_TYPE) { | |
1164 cached_stub = isolate()->builtins()->KeyedStoreIC_Slow(); | |
1165 } else { | |
1166 if (receiver_map->has_fast_elements() || | |
1167 receiver_map->has_external_array_elements() || | |
1168 receiver_map->has_fixed_typed_array_elements()) { | |
1169 cached_stub = StoreFastElementStub(isolate(), is_js_array, | |
1170 elements_kind, store_mode).GetCode(); | |
1171 } else { | |
1172 cached_stub = StoreElementStub(isolate(), is_js_array, elements_kind, | |
1173 store_mode).GetCode(); | |
1174 } | |
1175 } | |
1176 DCHECK(!cached_stub.is_null()); | |
1177 handlers.Add(cached_stub); | |
1178 transitioned_maps.Add(transitioned_map); | |
1179 } | |
1180 | |
1181 Handle<Code> code = CompileKeyedStorePolymorphic(receiver_maps, &handlers, | |
1182 &transitioned_maps); | |
1183 isolate()->counters()->keyed_store_polymorphic_stubs()->Increment(); | |
1184 PROFILE(isolate(), CodeCreateEvent(log_kind(code), *code, 0)); | |
1185 return code; | |
1186 } | |
1187 | |
1188 | |
1189 void ElementHandlerCompiler::GenerateStoreDictionaryElement( | |
1190 MacroAssembler* masm) { | |
1191 KeyedStoreIC::GenerateSlow(masm); | |
1192 } | |
1193 | |
1194 | |
1195 CallOptimization::CallOptimization(Handle<JSFunction> function) { | |
1196 Initialize(function); | |
1197 } | |
1198 | |
1199 | |
1200 Handle<JSObject> CallOptimization::LookupHolderOfExpectedType( | |
1201 Handle<Map> object_map, | |
1202 HolderLookup* holder_lookup) const { | |
1203 DCHECK(is_simple_api_call()); | |
1204 if (!object_map->IsJSObjectMap()) { | |
1205 *holder_lookup = kHolderNotFound; | |
1206 return Handle<JSObject>::null(); | |
1207 } | |
1208 if (expected_receiver_type_.is_null() || | |
1209 expected_receiver_type_->IsTemplateFor(*object_map)) { | |
1210 *holder_lookup = kHolderIsReceiver; | |
1211 return Handle<JSObject>::null(); | |
1212 } | |
1213 while (true) { | |
1214 if (!object_map->prototype()->IsJSObject()) break; | |
1215 Handle<JSObject> prototype(JSObject::cast(object_map->prototype())); | |
1216 if (!prototype->map()->is_hidden_prototype()) break; | |
1217 object_map = handle(prototype->map()); | |
1218 if (expected_receiver_type_->IsTemplateFor(*object_map)) { | |
1219 *holder_lookup = kHolderFound; | |
1220 return prototype; | |
1221 } | |
1222 } | |
1223 *holder_lookup = kHolderNotFound; | |
1224 return Handle<JSObject>::null(); | |
1225 } | |
1226 | |
1227 | |
1228 bool CallOptimization::IsCompatibleReceiver(Handle<Object> receiver, | |
1229 Handle<JSObject> holder) const { | |
1230 DCHECK(is_simple_api_call()); | |
1231 if (!receiver->IsJSObject()) return false; | |
1232 Handle<Map> map(JSObject::cast(*receiver)->map()); | |
1233 HolderLookup holder_lookup; | |
1234 Handle<JSObject> api_holder = | |
1235 LookupHolderOfExpectedType(map, &holder_lookup); | |
1236 switch (holder_lookup) { | |
1237 case kHolderNotFound: | |
1238 return false; | |
1239 case kHolderIsReceiver: | |
1240 return true; | |
1241 case kHolderFound: | |
1242 if (api_holder.is_identical_to(holder)) return true; | |
1243 // Check if holder is in prototype chain of api_holder. | |
1244 { | |
1245 JSObject* object = *api_holder; | |
1246 while (true) { | |
1247 Object* prototype = object->map()->prototype(); | |
1248 if (!prototype->IsJSObject()) return false; | |
1249 if (prototype == *holder) return true; | |
1250 object = JSObject::cast(prototype); | |
1251 } | |
1252 } | |
1253 break; | |
1254 } | |
1255 UNREACHABLE(); | |
1256 return false; | |
1257 } | |
1258 | |
1259 | |
1260 void CallOptimization::Initialize(Handle<JSFunction> function) { | |
1261 constant_function_ = Handle<JSFunction>::null(); | |
1262 is_simple_api_call_ = false; | |
1263 expected_receiver_type_ = Handle<FunctionTemplateInfo>::null(); | |
1264 api_call_info_ = Handle<CallHandlerInfo>::null(); | |
1265 | |
1266 if (function.is_null() || !function->is_compiled()) return; | |
1267 | |
1268 constant_function_ = function; | |
1269 AnalyzePossibleApiFunction(function); | |
1270 } | |
1271 | |
1272 | |
1273 void CallOptimization::AnalyzePossibleApiFunction(Handle<JSFunction> function) { | |
1274 if (!function->shared()->IsApiFunction()) return; | |
1275 Handle<FunctionTemplateInfo> info(function->shared()->get_api_func_data()); | |
1276 | |
1277 // Require a C++ callback. | |
1278 if (info->call_code()->IsUndefined()) return; | |
1279 api_call_info_ = | |
1280 Handle<CallHandlerInfo>(CallHandlerInfo::cast(info->call_code())); | |
1281 | |
1282 // Accept signatures that either have no restrictions at all or | |
1283 // only have restrictions on the receiver. | |
1284 if (!info->signature()->IsUndefined()) { | |
1285 Handle<SignatureInfo> signature = | |
1286 Handle<SignatureInfo>(SignatureInfo::cast(info->signature())); | |
1287 if (!signature->args()->IsUndefined()) return; | |
1288 if (!signature->receiver()->IsUndefined()) { | |
1289 expected_receiver_type_ = | |
1290 Handle<FunctionTemplateInfo>( | |
1291 FunctionTemplateInfo::cast(signature->receiver())); | |
1292 } | |
1293 } | |
1294 | |
1295 is_simple_api_call_ = true; | |
1296 } | |
1297 | |
1298 | |
1299 } } // namespace v8::internal | |
OLD | NEW |