blob: 81f89fd4bc19972ecf686123e2d45cfd15848871 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "api.h"
31#include "arguments.h"
32#include "ic-inl.h"
33#include "stub-cache.h"
34
35namespace v8 {
36namespace internal {
37
38// -----------------------------------------------------------------------
39// StubCache implementation.
40
41
42StubCache::Entry StubCache::primary_[StubCache::kPrimaryTableSize];
43StubCache::Entry StubCache::secondary_[StubCache::kSecondaryTableSize];
44
45void StubCache::Initialize(bool create_heap_objects) {
46 ASSERT(IsPowerOf2(kPrimaryTableSize));
47 ASSERT(IsPowerOf2(kSecondaryTableSize));
48 if (create_heap_objects) {
49 HandleScope scope;
50 Clear();
51 }
52}
53
54
55Code* StubCache::Set(String* name, Map* map, Code* code) {
56 // Get the flags from the code.
57 Code::Flags flags = Code::RemoveTypeFromFlags(code->flags());
58
59 // Validate that the name does not move on scavenge, and that we
60 // can use identity checks instead of string equality checks.
61 ASSERT(!Heap::InNewSpace(name));
62 ASSERT(name->IsSymbol());
63
64 // The state bits are not important to the hash function because
65 // the stub cache only contains monomorphic stubs. Make sure that
66 // the bits are the least significant so they will be the ones
67 // masked out.
68 ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC);
69 ASSERT(Code::kFlagsICStateShift == 0);
70
71 // Make sure that the code type is not included in the hash.
72 ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
73
74 // Compute the primary entry.
75 int primary_offset = PrimaryOffset(name, flags, map);
76 Entry* primary = entry(primary_, primary_offset);
77 Code* hit = primary->value;
78
79 // If the primary entry has useful data in it, we retire it to the
80 // secondary cache before overwriting it.
81 if (hit != Builtins::builtin(Builtins::Illegal)) {
82 Code::Flags primary_flags = Code::RemoveTypeFromFlags(hit->flags());
83 int secondary_offset =
84 SecondaryOffset(primary->key, primary_flags, primary_offset);
85 Entry* secondary = entry(secondary_, secondary_offset);
86 *secondary = *primary;
87 }
88
89 // Update primary cache.
90 primary->key = name;
91 primary->value = code;
92 return code;
93}
94
95
96Object* StubCache::ComputeLoadField(String* name,
97 JSObject* receiver,
98 JSObject* holder,
99 int field_index) {
100 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD);
101 Object* code = receiver->map()->FindInCodeCache(name, flags);
102 if (code->IsUndefined()) {
103 LoadStubCompiler compiler;
104 code = compiler.CompileLoadField(receiver, holder, field_index, name);
105 if (code->IsFailure()) return code;
106 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
107 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
108 if (result->IsFailure()) return code;
109 }
110 return Set(name, receiver->map(), Code::cast(code));
111}
112
113
114Object* StubCache::ComputeLoadCallback(String* name,
115 JSObject* receiver,
116 JSObject* holder,
117 AccessorInfo* callback) {
118 ASSERT(v8::ToCData<Address>(callback->getter()) != 0);
119 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS);
120 Object* code = receiver->map()->FindInCodeCache(name, flags);
121 if (code->IsUndefined()) {
122 LoadStubCompiler compiler;
Leon Clarkee46be812010-01-19 14:06:41 +0000123 code = compiler.CompileLoadCallback(name, receiver, holder, callback);
Steve Blocka7e24c12009-10-30 11:49:00 +0000124 if (code->IsFailure()) return code;
125 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
126 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
127 if (result->IsFailure()) return code;
128 }
129 return Set(name, receiver->map(), Code::cast(code));
130}
131
132
133Object* StubCache::ComputeLoadConstant(String* name,
134 JSObject* receiver,
135 JSObject* holder,
136 Object* value) {
137 Code::Flags flags =
138 Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION);
139 Object* code = receiver->map()->FindInCodeCache(name, flags);
140 if (code->IsUndefined()) {
141 LoadStubCompiler compiler;
142 code = compiler.CompileLoadConstant(receiver, holder, value, name);
143 if (code->IsFailure()) return code;
144 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
145 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
146 if (result->IsFailure()) return code;
147 }
148 return Set(name, receiver->map(), Code::cast(code));
149}
150
151
152Object* StubCache::ComputeLoadInterceptor(String* name,
153 JSObject* receiver,
154 JSObject* holder) {
155 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR);
156 Object* code = receiver->map()->FindInCodeCache(name, flags);
157 if (code->IsUndefined()) {
158 LoadStubCompiler compiler;
159 code = compiler.CompileLoadInterceptor(receiver, holder, name);
160 if (code->IsFailure()) return code;
161 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
162 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
163 if (result->IsFailure()) return code;
164 }
165 return Set(name, receiver->map(), Code::cast(code));
166}
167
168
169Object* StubCache::ComputeLoadNormal(String* name, JSObject* receiver) {
170 Code* code = Builtins::builtin(Builtins::LoadIC_Normal);
171 return Set(name, receiver->map(), code);
172}
173
174
175Object* StubCache::ComputeLoadGlobal(String* name,
176 JSObject* receiver,
177 GlobalObject* holder,
178 JSGlobalPropertyCell* cell,
179 bool is_dont_delete) {
180 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL);
181 Object* code = receiver->map()->FindInCodeCache(name, flags);
182 if (code->IsUndefined()) {
183 LoadStubCompiler compiler;
184 code = compiler.CompileLoadGlobal(receiver,
185 holder,
186 cell,
187 name,
188 is_dont_delete);
189 if (code->IsFailure()) return code;
190 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
191 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
192 if (result->IsFailure()) return code;
193 }
194 return Set(name, receiver->map(), Code::cast(code));
195}
196
197
198Object* StubCache::ComputeKeyedLoadField(String* name,
199 JSObject* receiver,
200 JSObject* holder,
201 int field_index) {
202 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD);
203 Object* code = receiver->map()->FindInCodeCache(name, flags);
204 if (code->IsUndefined()) {
205 KeyedLoadStubCompiler compiler;
206 code = compiler.CompileLoadField(name, receiver, holder, field_index);
207 if (code->IsFailure()) return code;
208 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
209 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
210 if (result->IsFailure()) return result;
211 }
212 return code;
213}
214
215
216Object* StubCache::ComputeKeyedLoadConstant(String* name,
217 JSObject* receiver,
218 JSObject* holder,
219 Object* value) {
220 Code::Flags flags =
221 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION);
222 Object* code = receiver->map()->FindInCodeCache(name, flags);
223 if (code->IsUndefined()) {
224 KeyedLoadStubCompiler compiler;
225 code = compiler.CompileLoadConstant(name, receiver, holder, value);
226 if (code->IsFailure()) return code;
227 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
228 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
229 if (result->IsFailure()) return result;
230 }
231 return code;
232}
233
234
235Object* StubCache::ComputeKeyedLoadInterceptor(String* name,
236 JSObject* receiver,
237 JSObject* holder) {
238 Code::Flags flags =
239 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR);
240 Object* code = receiver->map()->FindInCodeCache(name, flags);
241 if (code->IsUndefined()) {
242 KeyedLoadStubCompiler compiler;
243 code = compiler.CompileLoadInterceptor(receiver, holder, name);
244 if (code->IsFailure()) return code;
245 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
246 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
247 if (result->IsFailure()) return result;
248 }
249 return code;
250}
251
252
253Object* StubCache::ComputeKeyedLoadCallback(String* name,
254 JSObject* receiver,
255 JSObject* holder,
256 AccessorInfo* callback) {
257 Code::Flags flags =
258 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
259 Object* code = receiver->map()->FindInCodeCache(name, flags);
260 if (code->IsUndefined()) {
261 KeyedLoadStubCompiler compiler;
262 code = compiler.CompileLoadCallback(name, receiver, holder, callback);
263 if (code->IsFailure()) return code;
264 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
265 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
266 if (result->IsFailure()) return result;
267 }
268 return code;
269}
270
271
272
273Object* StubCache::ComputeKeyedLoadArrayLength(String* name,
274 JSArray* receiver) {
275 Code::Flags flags =
276 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
277 Object* code = receiver->map()->FindInCodeCache(name, flags);
278 if (code->IsUndefined()) {
279 KeyedLoadStubCompiler compiler;
280 code = compiler.CompileLoadArrayLength(name);
281 if (code->IsFailure()) return code;
282 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
283 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
284 if (result->IsFailure()) return result;
285 }
286 return code;
287}
288
289
290Object* StubCache::ComputeKeyedLoadStringLength(String* name,
291 String* receiver) {
292 Code::Flags flags =
293 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
294 Object* code = receiver->map()->FindInCodeCache(name, flags);
295 if (code->IsUndefined()) {
296 KeyedLoadStubCompiler compiler;
297 code = compiler.CompileLoadStringLength(name);
298 if (code->IsFailure()) return code;
299 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
300 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
301 if (result->IsFailure()) return result;
302 }
303 return code;
304}
305
306
307Object* StubCache::ComputeKeyedLoadFunctionPrototype(String* name,
308 JSFunction* receiver) {
309 Code::Flags flags =
310 Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
311 Object* code = receiver->map()->FindInCodeCache(name, flags);
312 if (code->IsUndefined()) {
313 KeyedLoadStubCompiler compiler;
314 code = compiler.CompileLoadFunctionPrototype(name);
315 if (code->IsFailure()) return code;
316 LOG(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
317 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
318 if (result->IsFailure()) return result;
319 }
320 return code;
321}
322
323
324Object* StubCache::ComputeStoreField(String* name,
325 JSObject* receiver,
326 int field_index,
327 Map* transition) {
328 PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION;
329 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, type);
330 Object* code = receiver->map()->FindInCodeCache(name, flags);
331 if (code->IsUndefined()) {
332 StoreStubCompiler compiler;
333 code = compiler.CompileStoreField(receiver, field_index, transition, name);
334 if (code->IsFailure()) return code;
335 LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name));
336 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
337 if (result->IsFailure()) return result;
338 }
339 return Set(name, receiver->map(), Code::cast(code));
340}
341
342
343Object* StubCache::ComputeStoreGlobal(String* name,
344 GlobalObject* receiver,
345 JSGlobalPropertyCell* cell) {
346 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, NORMAL);
347 Object* code = receiver->map()->FindInCodeCache(name, flags);
348 if (code->IsUndefined()) {
349 StoreStubCompiler compiler;
350 code = compiler.CompileStoreGlobal(receiver, cell, name);
351 if (code->IsFailure()) return code;
352 LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
353 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
354 if (result->IsFailure()) return code;
355 }
356 return Set(name, receiver->map(), Code::cast(code));
357}
358
359
360Object* StubCache::ComputeStoreCallback(String* name,
361 JSObject* receiver,
362 AccessorInfo* callback) {
363 ASSERT(v8::ToCData<Address>(callback->setter()) != 0);
364 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, CALLBACKS);
365 Object* code = receiver->map()->FindInCodeCache(name, flags);
366 if (code->IsUndefined()) {
367 StoreStubCompiler compiler;
368 code = compiler.CompileStoreCallback(receiver, callback, name);
369 if (code->IsFailure()) return code;
370 LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name));
371 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
372 if (result->IsFailure()) return result;
373 }
374 return Set(name, receiver->map(), Code::cast(code));
375}
376
377
378Object* StubCache::ComputeStoreInterceptor(String* name,
379 JSObject* receiver) {
380 Code::Flags flags =
381 Code::ComputeMonomorphicFlags(Code::STORE_IC, INTERCEPTOR);
382 Object* code = receiver->map()->FindInCodeCache(name, flags);
383 if (code->IsUndefined()) {
384 StoreStubCompiler compiler;
385 code = compiler.CompileStoreInterceptor(receiver, name);
386 if (code->IsFailure()) return code;
387 LOG(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name));
388 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
389 if (result->IsFailure()) return result;
390 }
391 return Set(name, receiver->map(), Code::cast(code));
392}
393
394
395Object* StubCache::ComputeKeyedStoreField(String* name, JSObject* receiver,
396 int field_index, Map* transition) {
397 PropertyType type = (transition == NULL) ? FIELD : MAP_TRANSITION;
398 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, type);
399 Object* code = receiver->map()->FindInCodeCache(name, flags);
400 if (code->IsUndefined()) {
401 KeyedStoreStubCompiler compiler;
402 code = compiler.CompileStoreField(receiver, field_index, transition, name);
403 if (code->IsFailure()) return code;
404 LOG(CodeCreateEvent(Logger::KEYED_STORE_IC_TAG, Code::cast(code), name));
405 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
406 if (result->IsFailure()) return result;
407 }
408 return code;
409}
410
411
412Object* StubCache::ComputeCallConstant(int argc,
413 InLoopFlag in_loop,
414 String* name,
415 Object* object,
416 JSObject* holder,
417 JSFunction* function) {
418 // Compute the check type and the map.
419 Map* map = IC::GetCodeCacheMapForObject(object);
420
421 // Compute check type based on receiver/holder.
422 StubCompiler::CheckType check = StubCompiler::RECEIVER_MAP_CHECK;
423 if (object->IsString()) {
424 check = StubCompiler::STRING_CHECK;
425 } else if (object->IsNumber()) {
426 check = StubCompiler::NUMBER_CHECK;
427 } else if (object->IsBoolean()) {
428 check = StubCompiler::BOOLEAN_CHECK;
429 }
430
431 Code::Flags flags =
432 Code::ComputeMonomorphicFlags(Code::CALL_IC,
433 CONSTANT_FUNCTION,
434 in_loop,
435 argc);
436 Object* code = map->FindInCodeCache(name, flags);
437 if (code->IsUndefined()) {
438 if (object->IsJSObject()) {
439 Object* opt =
440 Top::LookupSpecialFunction(JSObject::cast(object), holder, function);
441 if (opt->IsJSFunction()) {
442 check = StubCompiler::JSARRAY_HAS_FAST_ELEMENTS_CHECK;
443 function = JSFunction::cast(opt);
444 }
445 }
446 // If the function hasn't been compiled yet, we cannot do it now
447 // because it may cause GC. To avoid this issue, we return an
448 // internal error which will make sure we do not update any
449 // caches.
450 if (!function->is_compiled()) return Failure::InternalError();
451 // Compile the stub - only create stubs for fully compiled functions.
452 CallStubCompiler compiler(argc, in_loop);
453 code = compiler.CompileCallConstant(object, holder, function, name, check);
454 if (code->IsFailure()) return code;
455 ASSERT_EQ(flags, Code::cast(code)->flags());
456 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
457 Object* result = map->UpdateCodeCache(name, Code::cast(code));
458 if (result->IsFailure()) return result;
459 }
460 return Set(name, map, Code::cast(code));
461}
462
463
464Object* StubCache::ComputeCallField(int argc,
465 InLoopFlag in_loop,
466 String* name,
467 Object* object,
468 JSObject* holder,
469 int index) {
470 // Compute the check type and the map.
471 Map* map = IC::GetCodeCacheMapForObject(object);
472
473 // TODO(1233596): We cannot do receiver map check for non-JS objects
474 // because they may be represented as immediates without a
475 // map. Instead, we check against the map in the holder.
476 if (object->IsNumber() || object->IsBoolean() || object->IsString()) {
477 object = holder;
478 }
479
480 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
481 FIELD,
482 in_loop,
483 argc);
484 Object* code = map->FindInCodeCache(name, flags);
485 if (code->IsUndefined()) {
486 CallStubCompiler compiler(argc, in_loop);
487 code = compiler.CompileCallField(object, holder, index, name);
488 if (code->IsFailure()) return code;
489 ASSERT_EQ(flags, Code::cast(code)->flags());
490 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
491 Object* result = map->UpdateCodeCache(name, Code::cast(code));
492 if (result->IsFailure()) return result;
493 }
494 return Set(name, map, Code::cast(code));
495}
496
497
498Object* StubCache::ComputeCallInterceptor(int argc,
499 String* name,
500 Object* object,
501 JSObject* holder) {
502 // Compute the check type and the map.
503 // If the object is a value, we use the prototype map for the cache.
504 Map* map = IC::GetCodeCacheMapForObject(object);
505
506 // TODO(1233596): We cannot do receiver map check for non-JS objects
507 // because they may be represented as immediates without a
508 // map. Instead, we check against the map in the holder.
509 if (object->IsNumber() || object->IsBoolean() || object->IsString()) {
510 object = holder;
511 }
512
513 Code::Flags flags =
514 Code::ComputeMonomorphicFlags(Code::CALL_IC,
515 INTERCEPTOR,
516 NOT_IN_LOOP,
517 argc);
518 Object* code = map->FindInCodeCache(name, flags);
519 if (code->IsUndefined()) {
520 CallStubCompiler compiler(argc, NOT_IN_LOOP);
521 code = compiler.CompileCallInterceptor(object, holder, name);
522 if (code->IsFailure()) return code;
523 ASSERT_EQ(flags, Code::cast(code)->flags());
524 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
525 Object* result = map->UpdateCodeCache(name, Code::cast(code));
526 if (result->IsFailure()) return result;
527 }
528 return Set(name, map, Code::cast(code));
529}
530
531
532Object* StubCache::ComputeCallNormal(int argc,
533 InLoopFlag in_loop,
534 String* name,
535 JSObject* receiver) {
536 Object* code = ComputeCallNormal(argc, in_loop);
537 if (code->IsFailure()) return code;
538 return Set(name, receiver->map(), Code::cast(code));
539}
540
541
542Object* StubCache::ComputeCallGlobal(int argc,
543 InLoopFlag in_loop,
544 String* name,
545 JSObject* receiver,
546 GlobalObject* holder,
547 JSGlobalPropertyCell* cell,
548 JSFunction* function) {
549 Code::Flags flags =
550 Code::ComputeMonomorphicFlags(Code::CALL_IC, NORMAL, in_loop, argc);
551 Object* code = receiver->map()->FindInCodeCache(name, flags);
552 if (code->IsUndefined()) {
553 // If the function hasn't been compiled yet, we cannot do it now
554 // because it may cause GC. To avoid this issue, we return an
555 // internal error which will make sure we do not update any
556 // caches.
557 if (!function->is_compiled()) return Failure::InternalError();
558 CallStubCompiler compiler(argc, in_loop);
559 code = compiler.CompileCallGlobal(receiver, holder, cell, function, name);
560 if (code->IsFailure()) return code;
561 ASSERT_EQ(flags, Code::cast(code)->flags());
562 LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
563 Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
564 if (result->IsFailure()) return code;
565 }
566 return Set(name, receiver->map(), Code::cast(code));
567}
568
569
570static Object* GetProbeValue(Code::Flags flags) {
571 // Use raw_unchecked... so we don't get assert failures during GC.
572 NumberDictionary* dictionary = Heap::raw_unchecked_non_monomorphic_cache();
573 int entry = dictionary->FindEntry(flags);
574 if (entry != -1) return dictionary->ValueAt(entry);
575 return Heap::raw_unchecked_undefined_value();
576}
577
578
579static Object* ProbeCache(Code::Flags flags) {
580 Object* probe = GetProbeValue(flags);
581 if (probe != Heap::undefined_value()) return probe;
582 // Seed the cache with an undefined value to make sure that any
583 // generated code object can always be inserted into the cache
584 // without causing allocation failures.
585 Object* result =
586 Heap::non_monomorphic_cache()->AtNumberPut(flags,
587 Heap::undefined_value());
588 if (result->IsFailure()) return result;
589 Heap::public_set_non_monomorphic_cache(NumberDictionary::cast(result));
590 return probe;
591}
592
593
594static Object* FillCache(Object* code) {
595 if (code->IsCode()) {
596 int entry =
597 Heap::non_monomorphic_cache()->FindEntry(
598 Code::cast(code)->flags());
599 // The entry must be present see comment in ProbeCache.
600 ASSERT(entry != -1);
601 ASSERT(Heap::non_monomorphic_cache()->ValueAt(entry) ==
602 Heap::undefined_value());
603 Heap::non_monomorphic_cache()->ValueAtPut(entry, code);
604 CHECK(GetProbeValue(Code::cast(code)->flags()) == code);
605 }
606 return code;
607}
608
609
610Code* StubCache::FindCallInitialize(int argc, InLoopFlag in_loop) {
611 Code::Flags flags =
612 Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
613 Object* result = ProbeCache(flags);
614 ASSERT(!result->IsUndefined());
615 // This might be called during the marking phase of the collector
616 // hence the unchecked cast.
617 return reinterpret_cast<Code*>(result);
618}
619
620
621Object* StubCache::ComputeCallInitialize(int argc, InLoopFlag in_loop) {
622 Code::Flags flags =
623 Code::ComputeFlags(Code::CALL_IC, in_loop, UNINITIALIZED, NORMAL, argc);
624 Object* probe = ProbeCache(flags);
625 if (!probe->IsUndefined()) return probe;
626 StubCompiler compiler;
627 return FillCache(compiler.CompileCallInitialize(flags));
628}
629
630
631Object* StubCache::ComputeCallPreMonomorphic(int argc, InLoopFlag in_loop) {
632 Code::Flags flags =
633 Code::ComputeFlags(Code::CALL_IC, in_loop, PREMONOMORPHIC, NORMAL, argc);
634 Object* probe = ProbeCache(flags);
635 if (!probe->IsUndefined()) return probe;
636 StubCompiler compiler;
637 return FillCache(compiler.CompileCallPreMonomorphic(flags));
638}
639
640
641Object* StubCache::ComputeCallNormal(int argc, InLoopFlag in_loop) {
642 Code::Flags flags =
643 Code::ComputeFlags(Code::CALL_IC, in_loop, MONOMORPHIC, NORMAL, argc);
644 Object* probe = ProbeCache(flags);
645 if (!probe->IsUndefined()) return probe;
646 StubCompiler compiler;
647 return FillCache(compiler.CompileCallNormal(flags));
648}
649
650
651Object* StubCache::ComputeCallMegamorphic(int argc, InLoopFlag in_loop) {
652 Code::Flags flags =
653 Code::ComputeFlags(Code::CALL_IC, in_loop, MEGAMORPHIC, NORMAL, argc);
654 Object* probe = ProbeCache(flags);
655 if (!probe->IsUndefined()) return probe;
656 StubCompiler compiler;
657 return FillCache(compiler.CompileCallMegamorphic(flags));
658}
659
660
661Object* StubCache::ComputeCallMiss(int argc) {
662 Code::Flags flags =
663 Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, MEGAMORPHIC, NORMAL, argc);
664 Object* probe = ProbeCache(flags);
665 if (!probe->IsUndefined()) return probe;
666 StubCompiler compiler;
667 return FillCache(compiler.CompileCallMiss(flags));
668}
669
670
671#ifdef ENABLE_DEBUGGER_SUPPORT
672Object* StubCache::ComputeCallDebugBreak(int argc) {
673 Code::Flags flags =
674 Code::ComputeFlags(Code::CALL_IC, NOT_IN_LOOP, DEBUG_BREAK, NORMAL, argc);
675 Object* probe = ProbeCache(flags);
676 if (!probe->IsUndefined()) return probe;
677 StubCompiler compiler;
678 return FillCache(compiler.CompileCallDebugBreak(flags));
679}
680
681
682Object* StubCache::ComputeCallDebugPrepareStepIn(int argc) {
683 Code::Flags flags =
684 Code::ComputeFlags(Code::CALL_IC,
685 NOT_IN_LOOP,
686 DEBUG_PREPARE_STEP_IN,
687 NORMAL,
688 argc);
689 Object* probe = ProbeCache(flags);
690 if (!probe->IsUndefined()) return probe;
691 StubCompiler compiler;
692 return FillCache(compiler.CompileCallDebugPrepareStepIn(flags));
693}
694#endif
695
696
697Object* StubCache::ComputeLazyCompile(int argc) {
698 Code::Flags flags =
699 Code::ComputeFlags(Code::STUB, NOT_IN_LOOP, UNINITIALIZED, NORMAL, argc);
700 Object* probe = ProbeCache(flags);
701 if (!probe->IsUndefined()) return probe;
702 StubCompiler compiler;
703 Object* result = FillCache(compiler.CompileLazyCompile(flags));
704 if (result->IsCode()) {
705 Code* code = Code::cast(result);
706 USE(code);
707 LOG(CodeCreateEvent(Logger::LAZY_COMPILE_TAG,
708 code, code->arguments_count()));
709 }
710 return result;
711}
712
713
714void StubCache::Clear() {
715 for (int i = 0; i < kPrimaryTableSize; i++) {
716 primary_[i].key = Heap::empty_string();
717 primary_[i].value = Builtins::builtin(Builtins::Illegal);
718 }
719 for (int j = 0; j < kSecondaryTableSize; j++) {
720 secondary_[j].key = Heap::empty_string();
721 secondary_[j].value = Builtins::builtin(Builtins::Illegal);
722 }
723}
724
725
726// ------------------------------------------------------------------------
727// StubCompiler implementation.
728
729
730// Support function for computing call IC miss stubs.
731Handle<Code> ComputeCallMiss(int argc) {
732 CALL_HEAP_FUNCTION(StubCache::ComputeCallMiss(argc), Code);
733}
734
735
736
737Object* LoadCallbackProperty(Arguments args) {
Steve Blockd0582a62009-12-15 09:54:21 +0000738 ASSERT(args[0]->IsJSObject());
739 ASSERT(args[1]->IsJSObject());
Steve Blocka7e24c12009-10-30 11:49:00 +0000740 AccessorInfo* callback = AccessorInfo::cast(args[2]);
741 Address getter_address = v8::ToCData<Address>(callback->getter());
742 v8::AccessorGetter fun = FUNCTION_CAST<v8::AccessorGetter>(getter_address);
743 ASSERT(fun != NULL);
Steve Blockd0582a62009-12-15 09:54:21 +0000744 CustomArguments custom_args(callback->data(),
745 JSObject::cast(args[0]),
746 JSObject::cast(args[1]));
747 v8::AccessorInfo info(custom_args.end());
Steve Blocka7e24c12009-10-30 11:49:00 +0000748 HandleScope scope;
749 v8::Handle<v8::Value> result;
750 {
751 // Leaving JavaScript.
752 VMState state(EXTERNAL);
Steve Blockd0582a62009-12-15 09:54:21 +0000753#ifdef ENABLE_LOGGING_AND_PROFILING
754 state.set_external_callback(getter_address);
755#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000756 result = fun(v8::Utils::ToLocal(args.at<String>(4)), info);
757 }
758 RETURN_IF_SCHEDULED_EXCEPTION();
759 if (result.IsEmpty()) return Heap::undefined_value();
760 return *v8::Utils::OpenHandle(*result);
761}
762
763
764Object* StoreCallbackProperty(Arguments args) {
765 JSObject* recv = JSObject::cast(args[0]);
766 AccessorInfo* callback = AccessorInfo::cast(args[1]);
767 Address setter_address = v8::ToCData<Address>(callback->setter());
768 v8::AccessorSetter fun = FUNCTION_CAST<v8::AccessorSetter>(setter_address);
769 ASSERT(fun != NULL);
770 Handle<String> name = args.at<String>(2);
771 Handle<Object> value = args.at<Object>(3);
772 HandleScope scope;
773 LOG(ApiNamedPropertyAccess("store", recv, *name));
774 CustomArguments custom_args(callback->data(), recv, recv);
775 v8::AccessorInfo info(custom_args.end());
776 {
777 // Leaving JavaScript.
778 VMState state(EXTERNAL);
Steve Blockd0582a62009-12-15 09:54:21 +0000779#ifdef ENABLE_LOGGING_AND_PROFILING
780 state.set_external_callback(setter_address);
781#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000782 fun(v8::Utils::ToLocal(name), v8::Utils::ToLocal(value), info);
783 }
784 RETURN_IF_SCHEDULED_EXCEPTION();
785 return *value;
786}
787
788/**
789 * Attempts to load a property with an interceptor (which must be present),
790 * but doesn't search the prototype chain.
791 *
792 * Returns |Heap::no_interceptor_result_sentinel()| if interceptor doesn't
793 * provide any value for the given name.
794 */
795Object* LoadPropertyWithInterceptorOnly(Arguments args) {
796 JSObject* receiver_handle = JSObject::cast(args[0]);
797 JSObject* holder_handle = JSObject::cast(args[1]);
798 Handle<String> name_handle = args.at<String>(2);
799 Handle<InterceptorInfo> interceptor_info = args.at<InterceptorInfo>(3);
800 Object* data_handle = args[4];
801
802 Address getter_address = v8::ToCData<Address>(interceptor_info->getter());
803 v8::NamedPropertyGetter getter =
804 FUNCTION_CAST<v8::NamedPropertyGetter>(getter_address);
805 ASSERT(getter != NULL);
806
807 {
808 // Use the interceptor getter.
809 CustomArguments args(data_handle, receiver_handle, holder_handle);
810 v8::AccessorInfo info(args.end());
811 HandleScope scope;
812 v8::Handle<v8::Value> r;
813 {
814 // Leaving JavaScript.
815 VMState state(EXTERNAL);
816 r = getter(v8::Utils::ToLocal(name_handle), info);
817 }
818 RETURN_IF_SCHEDULED_EXCEPTION();
819 if (!r.IsEmpty()) {
820 return *v8::Utils::OpenHandle(*r);
821 }
822 }
823
824 return Heap::no_interceptor_result_sentinel();
825}
826
827
828static Object* ThrowReferenceError(String* name) {
829 // If the load is non-contextual, just return the undefined result.
830 // Note that both keyed and non-keyed loads may end up here, so we
831 // can't use either LoadIC or KeyedLoadIC constructors.
832 IC ic(IC::NO_EXTRA_FRAME);
833 ASSERT(ic.target()->is_load_stub() || ic.target()->is_keyed_load_stub());
Leon Clarkee46be812010-01-19 14:06:41 +0000834 if (!ic.SlowIsContextual()) return Heap::undefined_value();
Steve Blocka7e24c12009-10-30 11:49:00 +0000835
836 // Throw a reference error.
837 HandleScope scope;
838 Handle<String> name_handle(name);
839 Handle<Object> error =
840 Factory::NewReferenceError("not_defined",
841 HandleVector(&name_handle, 1));
842 return Top::Throw(*error);
843}
844
845
846static Object* LoadWithInterceptor(Arguments* args,
847 PropertyAttributes* attrs) {
848 Handle<JSObject> receiver_handle = args->at<JSObject>(0);
849 Handle<JSObject> holder_handle = args->at<JSObject>(1);
850 Handle<String> name_handle = args->at<String>(2);
851 Handle<InterceptorInfo> interceptor_info = args->at<InterceptorInfo>(3);
852 Handle<Object> data_handle = args->at<Object>(4);
853
854 Address getter_address = v8::ToCData<Address>(interceptor_info->getter());
855 v8::NamedPropertyGetter getter =
856 FUNCTION_CAST<v8::NamedPropertyGetter>(getter_address);
857 ASSERT(getter != NULL);
858
859 {
860 // Use the interceptor getter.
861 CustomArguments args(*data_handle, *receiver_handle, *holder_handle);
862 v8::AccessorInfo info(args.end());
863 HandleScope scope;
864 v8::Handle<v8::Value> r;
865 {
866 // Leaving JavaScript.
867 VMState state(EXTERNAL);
868 r = getter(v8::Utils::ToLocal(name_handle), info);
869 }
870 RETURN_IF_SCHEDULED_EXCEPTION();
871 if (!r.IsEmpty()) {
872 *attrs = NONE;
873 return *v8::Utils::OpenHandle(*r);
874 }
875 }
876
877 Object* result = holder_handle->GetPropertyPostInterceptor(
878 *receiver_handle,
879 *name_handle,
880 attrs);
881 RETURN_IF_SCHEDULED_EXCEPTION();
882 return result;
883}
884
885
886/**
887 * Loads a property with an interceptor performing post interceptor
888 * lookup if interceptor failed.
889 */
890Object* LoadPropertyWithInterceptorForLoad(Arguments args) {
891 PropertyAttributes attr = NONE;
892 Object* result = LoadWithInterceptor(&args, &attr);
893 if (result->IsFailure()) return result;
894
895 // If the property is present, return it.
896 if (attr != ABSENT) return result;
897 return ThrowReferenceError(String::cast(args[2]));
898}
899
900
901Object* LoadPropertyWithInterceptorForCall(Arguments args) {
902 PropertyAttributes attr;
903 Object* result = LoadWithInterceptor(&args, &attr);
904 RETURN_IF_SCHEDULED_EXCEPTION();
905 // This is call IC. In this case, we simply return the undefined result which
906 // will lead to an exception when trying to invoke the result as a
907 // function.
908 return result;
909}
910
911
912Object* StoreInterceptorProperty(Arguments args) {
913 JSObject* recv = JSObject::cast(args[0]);
914 String* name = String::cast(args[1]);
915 Object* value = args[2];
916 ASSERT(recv->HasNamedInterceptor());
917 PropertyAttributes attr = NONE;
918 Object* result = recv->SetPropertyWithInterceptor(name, value, attr);
919 return result;
920}
921
922
923Object* StubCompiler::CompileCallInitialize(Code::Flags flags) {
924 HandleScope scope;
925 int argc = Code::ExtractArgumentsCountFromFlags(flags);
926 CallIC::GenerateInitialize(masm(), argc);
927 Object* result = GetCodeWithFlags(flags, "CompileCallInitialize");
928 if (!result->IsFailure()) {
929 Counters::call_initialize_stubs.Increment();
930 Code* code = Code::cast(result);
931 USE(code);
932 LOG(CodeCreateEvent(Logger::CALL_INITIALIZE_TAG,
933 code, code->arguments_count()));
934 }
935 return result;
936}
937
938
939Object* StubCompiler::CompileCallPreMonomorphic(Code::Flags flags) {
940 HandleScope scope;
941 int argc = Code::ExtractArgumentsCountFromFlags(flags);
942 // The code of the PreMonomorphic stub is the same as the code
943 // of the Initialized stub. They just differ on the code object flags.
944 CallIC::GenerateInitialize(masm(), argc);
945 Object* result = GetCodeWithFlags(flags, "CompileCallPreMonomorphic");
946 if (!result->IsFailure()) {
947 Counters::call_premonomorphic_stubs.Increment();
948 Code* code = Code::cast(result);
949 USE(code);
950 LOG(CodeCreateEvent(Logger::CALL_PRE_MONOMORPHIC_TAG,
951 code, code->arguments_count()));
952 }
953 return result;
954}
955
956
957Object* StubCompiler::CompileCallNormal(Code::Flags flags) {
958 HandleScope scope;
959 int argc = Code::ExtractArgumentsCountFromFlags(flags);
960 CallIC::GenerateNormal(masm(), argc);
961 Object* result = GetCodeWithFlags(flags, "CompileCallNormal");
962 if (!result->IsFailure()) {
963 Counters::call_normal_stubs.Increment();
964 Code* code = Code::cast(result);
965 USE(code);
966 LOG(CodeCreateEvent(Logger::CALL_NORMAL_TAG,
967 code, code->arguments_count()));
968 }
969 return result;
970}
971
972
973Object* StubCompiler::CompileCallMegamorphic(Code::Flags flags) {
974 HandleScope scope;
975 int argc = Code::ExtractArgumentsCountFromFlags(flags);
976 CallIC::GenerateMegamorphic(masm(), argc);
977 Object* result = GetCodeWithFlags(flags, "CompileCallMegamorphic");
978 if (!result->IsFailure()) {
979 Counters::call_megamorphic_stubs.Increment();
980 Code* code = Code::cast(result);
981 USE(code);
982 LOG(CodeCreateEvent(Logger::CALL_MEGAMORPHIC_TAG,
983 code, code->arguments_count()));
984 }
985 return result;
986}
987
988
989Object* StubCompiler::CompileCallMiss(Code::Flags flags) {
990 HandleScope scope;
991 int argc = Code::ExtractArgumentsCountFromFlags(flags);
992 CallIC::GenerateMiss(masm(), argc);
993 Object* result = GetCodeWithFlags(flags, "CompileCallMiss");
994 if (!result->IsFailure()) {
995 Counters::call_megamorphic_stubs.Increment();
996 Code* code = Code::cast(result);
997 USE(code);
998 LOG(CodeCreateEvent(Logger::CALL_MISS_TAG, code, code->arguments_count()));
999 }
1000 return result;
1001}
1002
1003
1004#ifdef ENABLE_DEBUGGER_SUPPORT
1005Object* StubCompiler::CompileCallDebugBreak(Code::Flags flags) {
1006 HandleScope scope;
1007 Debug::GenerateCallICDebugBreak(masm());
1008 Object* result = GetCodeWithFlags(flags, "CompileCallDebugBreak");
1009 if (!result->IsFailure()) {
1010 Code* code = Code::cast(result);
1011 USE(code);
1012 LOG(CodeCreateEvent(Logger::CALL_DEBUG_BREAK_TAG,
1013 code, code->arguments_count()));
1014 }
1015 return result;
1016}
1017
1018
1019Object* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) {
1020 HandleScope scope;
1021 // Use the same code for the the step in preparations as we do for
1022 // the miss case.
1023 int argc = Code::ExtractArgumentsCountFromFlags(flags);
1024 CallIC::GenerateMiss(masm(), argc);
1025 Object* result = GetCodeWithFlags(flags, "CompileCallDebugPrepareStepIn");
1026 if (!result->IsFailure()) {
1027 Code* code = Code::cast(result);
1028 USE(code);
1029 LOG(CodeCreateEvent(Logger::CALL_DEBUG_PREPARE_STEP_IN_TAG,
1030 code, code->arguments_count()));
1031 }
1032 return result;
1033}
1034#endif
1035
1036
1037Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) {
1038 // Check for allocation failures during stub compilation.
1039 if (failure_->IsFailure()) return failure_;
1040
1041 // Create code object in the heap.
1042 CodeDesc desc;
1043 masm_.GetCode(&desc);
1044 Object* result = Heap::CreateCode(desc, NULL, flags, masm_.CodeObject());
1045#ifdef ENABLE_DISASSEMBLER
1046 if (FLAG_print_code_stubs && !result->IsFailure()) {
1047 Code::cast(result)->Disassemble(name);
1048 }
1049#endif
1050 return result;
1051}
1052
1053
1054Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, String* name) {
1055 if (FLAG_print_code_stubs && (name != NULL)) {
1056 return GetCodeWithFlags(flags, *name->ToCString());
1057 }
1058 return GetCodeWithFlags(flags, reinterpret_cast<char*>(NULL));
1059}
1060
Leon Clarke4515c472010-02-03 11:58:03 +00001061void StubCompiler::LookupPostInterceptor(JSObject* holder,
1062 String* name,
1063 LookupResult* lookup) {
1064 holder->LocalLookupRealNamedProperty(name, lookup);
1065 if (lookup->IsNotFound()) {
1066 Object* proto = holder->GetPrototype();
1067 if (proto != Heap::null_value()) {
1068 proto->Lookup(name, lookup);
1069 }
1070 }
1071}
1072
1073
Steve Blocka7e24c12009-10-30 11:49:00 +00001074
1075Object* LoadStubCompiler::GetCode(PropertyType type, String* name) {
1076 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, type);
1077 return GetCodeWithFlags(flags, name);
1078}
1079
1080
1081Object* KeyedLoadStubCompiler::GetCode(PropertyType type, String* name) {
1082 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, type);
1083 return GetCodeWithFlags(flags, name);
1084}
1085
1086
1087Object* StoreStubCompiler::GetCode(PropertyType type, String* name) {
1088 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::STORE_IC, type);
1089 return GetCodeWithFlags(flags, name);
1090}
1091
1092
1093Object* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) {
1094 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_STORE_IC, type);
1095 return GetCodeWithFlags(flags, name);
1096}
1097
1098
1099Object* CallStubCompiler::GetCode(PropertyType type, String* name) {
1100 int argc = arguments_.immediate();
1101 Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC,
1102 type,
1103 in_loop_,
1104 argc);
1105 return GetCodeWithFlags(flags, name);
1106}
1107
1108
1109Object* ConstructStubCompiler::GetCode() {
1110 Code::Flags flags = Code::ComputeFlags(Code::STUB);
1111 Object* result = GetCodeWithFlags(flags, "ConstructStub");
1112 if (!result->IsFailure()) {
1113 Code* code = Code::cast(result);
1114 USE(code);
1115 LOG(CodeCreateEvent(Logger::STUB_TAG, code, "ConstructStub"));
1116 }
1117 return result;
1118}
1119
1120
1121} } // namespace v8::internal