blob: a5370a6f854a18576e8d153943572de63402f4d1 [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 "accessors.h"
31#include "api.h"
32#include "arguments.h"
33#include "execution.h"
34#include "ic-inl.h"
35#include "runtime.h"
36#include "stub-cache.h"
37
38namespace v8 {
39namespace internal {
40
41#ifdef DEBUG
42static char TransitionMarkFromState(IC::State state) {
43 switch (state) {
44 case UNINITIALIZED: return '0';
45 case PREMONOMORPHIC: return 'P';
46 case MONOMORPHIC: return '1';
47 case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
48 case MEGAMORPHIC: return 'N';
49
50 // We never see the debugger states here, because the state is
51 // computed from the original code - not the patched code. Let
52 // these cases fall through to the unreachable code below.
53 case DEBUG_BREAK: break;
54 case DEBUG_PREPARE_STEP_IN: break;
55 }
56 UNREACHABLE();
57 return 0;
58}
59
60void IC::TraceIC(const char* type,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +010061 Handle<Object> name,
Steve Blocka7e24c12009-10-30 11:49:00 +000062 State old_state,
63 Code* new_target,
64 const char* extra_info) {
65 if (FLAG_trace_ic) {
Steve Block6ded16b2010-05-10 14:33:55 +010066 State new_state = StateFrom(new_target,
67 Heap::undefined_value(),
68 Heap::undefined_value());
Steve Blocka7e24c12009-10-30 11:49:00 +000069 PrintF("[%s (%c->%c)%s", type,
70 TransitionMarkFromState(old_state),
71 TransitionMarkFromState(new_state),
72 extra_info);
73 name->Print();
74 PrintF("]\n");
75 }
76}
77#endif
78
79
80IC::IC(FrameDepth depth) {
81 // To improve the performance of the (much used) IC code, we unfold
82 // a few levels of the stack frame iteration code. This yields a
83 // ~35% speedup when running DeltaBlue with the '--nouse-ic' flag.
84 const Address entry = Top::c_entry_fp(Top::GetCurrentThread());
85 Address* pc_address =
86 reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset);
87 Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
88 // If there's another JavaScript frame on the stack, we need to look
89 // one frame further down the stack to find the frame pointer and
90 // the return address stack slot.
91 if (depth == EXTRA_CALL_FRAME) {
92 const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset;
93 pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset);
94 fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
95 }
96#ifdef DEBUG
97 StackFrameIterator it;
98 for (int i = 0; i < depth + 1; i++) it.Advance();
99 StackFrame* frame = it.frame();
100 ASSERT(fp == frame->fp() && pc_address == frame->pc_address());
101#endif
102 fp_ = fp;
103 pc_address_ = pc_address;
104}
105
106
107#ifdef ENABLE_DEBUGGER_SUPPORT
108Address IC::OriginalCodeAddress() {
109 HandleScope scope;
110 // Compute the JavaScript frame for the frame pointer of this IC
111 // structure. We need this to be able to find the function
112 // corresponding to the frame.
113 StackFrameIterator it;
114 while (it.frame()->fp() != this->fp()) it.Advance();
115 JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
116 // Find the function on the stack and both the active code for the
117 // function and the original code.
118 JSFunction* function = JSFunction::cast(frame->function());
119 Handle<SharedFunctionInfo> shared(function->shared());
120 Code* code = shared->code();
121 ASSERT(Debug::HasDebugInfo(shared));
122 Code* original_code = Debug::GetDebugInfo(shared)->original_code();
123 ASSERT(original_code->IsCode());
124 // Get the address of the call site in the active code. This is the
125 // place where the call to DebugBreakXXX is and where the IC
126 // normally would be.
127 Address addr = pc() - Assembler::kCallTargetAddressOffset;
128 // Return the address in the original code. This is the place where
129 // the call which has been overwritten by the DebugBreakXXX resides
130 // and the place where the inline cache system should look.
Steve Blockd0582a62009-12-15 09:54:21 +0000131 intptr_t delta =
132 original_code->instruction_start() - code->instruction_start();
Steve Blocka7e24c12009-10-30 11:49:00 +0000133 return addr + delta;
134}
135#endif
136
Steve Block8defd9f2010-07-08 12:39:36 +0100137
138static bool HasNormalObjectsInPrototypeChain(LookupResult* lookup,
139 Object* receiver) {
140 Object* end = lookup->IsProperty() ? lookup->holder() : Heap::null_value();
141 for (Object* current = receiver;
142 current != end;
143 current = current->GetPrototype()) {
144 if (current->IsJSObject() &&
145 !JSObject::cast(current)->HasFastProperties() &&
146 !current->IsJSGlobalProxy() &&
147 !current->IsJSGlobalObject()) {
148 return true;
149 }
150 }
151
152 return false;
153}
154
155
Steve Block6ded16b2010-05-10 14:33:55 +0100156IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000157 IC::State state = target->ic_state();
158
159 if (state != MONOMORPHIC) return state;
160 if (receiver->IsUndefined() || receiver->IsNull()) return state;
161
Steve Block8defd9f2010-07-08 12:39:36 +0100162 InlineCacheHolderFlag cache_holder =
163 Code::ExtractCacheHolderFromFlags(target->flags());
164
165
166 if (cache_holder == OWN_MAP && !receiver->IsJSObject()) {
167 // The stub was generated for JSObject but called for non-JSObject.
168 // IC::GetCodeCacheMap is not applicable.
169 return MONOMORPHIC;
170 } else if (cache_holder == PROTOTYPE_MAP &&
171 receiver->GetPrototype()->IsNull()) {
172 // IC::GetCodeCacheMap is not applicable.
173 return MONOMORPHIC;
174 }
175 Map* map = IC::GetCodeCacheMap(receiver, cache_holder);
Steve Blocka7e24c12009-10-30 11:49:00 +0000176
177 // Decide whether the inline cache failed because of changes to the
178 // receiver itself or changes to one of its prototypes.
179 //
180 // If there are changes to the receiver itself, the map of the
181 // receiver will have changed and the current target will not be in
182 // the receiver map's code cache. Therefore, if the current target
183 // is in the receiver map's code cache, the inline cache failed due
184 // to prototype check failure.
Steve Block6ded16b2010-05-10 14:33:55 +0100185 int index = map->IndexInCodeCache(name, target);
Steve Blocka7e24c12009-10-30 11:49:00 +0000186 if (index >= 0) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100187 // For keyed load/store/call, the most likely cause of cache failure is
Steve Blocka7e24c12009-10-30 11:49:00 +0000188 // that the key has changed. We do not distinguish between
189 // prototype and non-prototype failures for keyed access.
190 Code::Kind kind = target->kind();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100191 if (kind == Code::KEYED_LOAD_IC ||
192 kind == Code::KEYED_STORE_IC ||
193 kind == Code::KEYED_CALL_IC) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000194 return MONOMORPHIC;
195 }
196
197 // Remove the target from the code cache to avoid hitting the same
198 // invalid stub again.
Steve Block6ded16b2010-05-10 14:33:55 +0100199 map->RemoveFromCodeCache(String::cast(name), target, index);
Steve Blocka7e24c12009-10-30 11:49:00 +0000200
201 return MONOMORPHIC_PROTOTYPE_FAILURE;
202 }
203
204 // The builtins object is special. It only changes when JavaScript
205 // builtins are loaded lazily. It is important to keep inline
206 // caches for the builtins object monomorphic. Therefore, if we get
207 // an inline cache miss for the builtins object after lazily loading
208 // JavaScript builtins, we return uninitialized as the state to
209 // force the inline cache back to monomorphic state.
210 if (receiver->IsJSBuiltinsObject()) {
211 return UNINITIALIZED;
212 }
213
214 return MONOMORPHIC;
215}
216
217
218RelocInfo::Mode IC::ComputeMode() {
219 Address addr = address();
220 Code* code = Code::cast(Heap::FindCodeObject(addr));
221 for (RelocIterator it(code, RelocInfo::kCodeTargetMask);
222 !it.done(); it.next()) {
223 RelocInfo* info = it.rinfo();
224 if (info->pc() == addr) return info->rmode();
225 }
226 UNREACHABLE();
227 return RelocInfo::NONE;
228}
229
230
231Failure* IC::TypeError(const char* type,
232 Handle<Object> object,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100233 Handle<Object> key) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000234 HandleScope scope;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100235 Handle<Object> args[2] = { key, object };
Steve Blocka7e24c12009-10-30 11:49:00 +0000236 Handle<Object> error = Factory::NewTypeError(type, HandleVector(args, 2));
237 return Top::Throw(*error);
238}
239
240
241Failure* IC::ReferenceError(const char* type, Handle<String> name) {
242 HandleScope scope;
243 Handle<Object> error =
244 Factory::NewReferenceError(type, HandleVector(&name, 1));
245 return Top::Throw(*error);
246}
247
248
249void IC::Clear(Address address) {
250 Code* target = GetTargetAtAddress(address);
251
252 // Don't clear debug break inline cache as it will remove the break point.
253 if (target->ic_state() == DEBUG_BREAK) return;
254
255 switch (target->kind()) {
256 case Code::LOAD_IC: return LoadIC::Clear(address, target);
257 case Code::KEYED_LOAD_IC: return KeyedLoadIC::Clear(address, target);
258 case Code::STORE_IC: return StoreIC::Clear(address, target);
259 case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
260 case Code::CALL_IC: return CallIC::Clear(address, target);
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100261 case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
Steve Block6ded16b2010-05-10 14:33:55 +0100262 case Code::BINARY_OP_IC: return; // Clearing these is tricky and does not
263 // make any performance difference.
Steve Blocka7e24c12009-10-30 11:49:00 +0000264 default: UNREACHABLE();
265 }
266}
267
268
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100269void CallICBase::Clear(Address address, Code* target) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000270 State state = target->ic_state();
Steve Blocka7e24c12009-10-30 11:49:00 +0000271 if (state == UNINITIALIZED) return;
272 Code* code =
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100273 StubCache::FindCallInitialize(target->arguments_count(),
274 target->ic_in_loop(),
275 target->kind());
Steve Blocka7e24c12009-10-30 11:49:00 +0000276 SetTargetAtAddress(address, code);
277}
278
279
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100280void KeyedLoadIC::ClearInlinedVersion(Address address) {
281 // Insert null as the map to check for to make sure the map check fails
282 // sending control flow to the IC instead of the inlined version.
283 PatchInlinedLoad(address, Heap::null_value());
284}
285
286
Steve Blocka7e24c12009-10-30 11:49:00 +0000287void KeyedLoadIC::Clear(Address address, Code* target) {
288 if (target->ic_state() == UNINITIALIZED) return;
289 // Make sure to also clear the map used in inline fast cases. If we
290 // do not clear these maps, cached code can keep objects alive
291 // through the embedded maps.
292 ClearInlinedVersion(address);
293 SetTargetAtAddress(address, initialize_stub());
294}
295
296
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100297void LoadIC::ClearInlinedVersion(Address address) {
298 // Reset the map check of the inlined inobject property load (if
299 // present) to guarantee failure by holding an invalid map (the null
300 // value). The offset can be patched to anything.
301 PatchInlinedLoad(address, Heap::null_value(), 0);
302}
303
304
Steve Blocka7e24c12009-10-30 11:49:00 +0000305void LoadIC::Clear(Address address, Code* target) {
306 if (target->ic_state() == UNINITIALIZED) return;
307 ClearInlinedVersion(address);
308 SetTargetAtAddress(address, initialize_stub());
309}
310
311
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100312void StoreIC::ClearInlinedVersion(Address address) {
313 // Reset the map check of the inlined inobject property store (if
314 // present) to guarantee failure by holding an invalid map (the null
315 // value). The offset can be patched to anything.
316 PatchInlinedStore(address, Heap::null_value(), 0);
317}
318
319
Steve Blocka7e24c12009-10-30 11:49:00 +0000320void StoreIC::Clear(Address address, Code* target) {
321 if (target->ic_state() == UNINITIALIZED) return;
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100322 ClearInlinedVersion(address);
Steve Blocka7e24c12009-10-30 11:49:00 +0000323 SetTargetAtAddress(address, initialize_stub());
324}
325
326
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100327void KeyedStoreIC::ClearInlinedVersion(Address address) {
328 // Insert null as the elements map to check for. This will make
329 // sure that the elements fast-case map check fails so that control
330 // flows to the IC instead of the inlined version.
331 PatchInlinedStore(address, Heap::null_value());
332}
333
334
335void KeyedStoreIC::RestoreInlinedVersion(Address address) {
336 // Restore the fast-case elements map check so that the inlined
337 // version can be used again.
338 PatchInlinedStore(address, Heap::fixed_array_map());
339}
340
341
Steve Blocka7e24c12009-10-30 11:49:00 +0000342void KeyedStoreIC::Clear(Address address, Code* target) {
343 if (target->ic_state() == UNINITIALIZED) return;
344 SetTargetAtAddress(address, initialize_stub());
345}
346
347
Steve Block3ce2e202009-11-05 08:53:23 +0000348Code* KeyedLoadIC::external_array_stub(JSObject::ElementsKind elements_kind) {
349 switch (elements_kind) {
350 case JSObject::EXTERNAL_BYTE_ELEMENTS:
351 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalByteArray);
352 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
353 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedByteArray);
354 case JSObject::EXTERNAL_SHORT_ELEMENTS:
355 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalShortArray);
356 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
357 return Builtins::builtin(
358 Builtins::KeyedLoadIC_ExternalUnsignedShortArray);
359 case JSObject::EXTERNAL_INT_ELEMENTS:
360 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalIntArray);
361 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
362 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedIntArray);
363 case JSObject::EXTERNAL_FLOAT_ELEMENTS:
364 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalFloatArray);
365 default:
366 UNREACHABLE();
367 return NULL;
368 }
369}
370
371
372Code* KeyedStoreIC::external_array_stub(JSObject::ElementsKind elements_kind) {
373 switch (elements_kind) {
374 case JSObject::EXTERNAL_BYTE_ELEMENTS:
375 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalByteArray);
376 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
377 return Builtins::builtin(
378 Builtins::KeyedStoreIC_ExternalUnsignedByteArray);
379 case JSObject::EXTERNAL_SHORT_ELEMENTS:
380 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalShortArray);
381 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
382 return Builtins::builtin(
383 Builtins::KeyedStoreIC_ExternalUnsignedShortArray);
384 case JSObject::EXTERNAL_INT_ELEMENTS:
385 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalIntArray);
386 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
387 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalUnsignedIntArray);
388 case JSObject::EXTERNAL_FLOAT_ELEMENTS:
389 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalFloatArray);
390 default:
391 UNREACHABLE();
392 return NULL;
393 }
394}
395
396
Steve Blocka7e24c12009-10-30 11:49:00 +0000397static bool HasInterceptorGetter(JSObject* object) {
398 return !object->GetNamedInterceptor()->getter()->IsUndefined();
399}
400
401
402static void LookupForRead(Object* object,
403 String* name,
404 LookupResult* lookup) {
405 AssertNoAllocation no_gc; // pointers must stay valid
406
407 // Skip all the objects with named interceptors, but
408 // without actual getter.
409 while (true) {
410 object->Lookup(name, lookup);
411 // Besides normal conditions (property not found or it's not
Andrei Popescu402d9372010-02-26 13:31:12 +0000412 // an interceptor), bail out if lookup is not cacheable: we won't
Steve Blocka7e24c12009-10-30 11:49:00 +0000413 // be able to IC it anyway and regular lookup should work fine.
Andrei Popescu402d9372010-02-26 13:31:12 +0000414 if (!lookup->IsFound()
415 || (lookup->type() != INTERCEPTOR)
416 || !lookup->IsCacheable()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000417 return;
418 }
419
420 JSObject* holder = lookup->holder();
421 if (HasInterceptorGetter(holder)) {
422 return;
423 }
424
425 holder->LocalLookupRealNamedProperty(name, lookup);
Andrei Popescu402d9372010-02-26 13:31:12 +0000426 if (lookup->IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000427 ASSERT(lookup->type() != INTERCEPTOR);
428 return;
429 }
430
431 Object* proto = holder->GetPrototype();
432 if (proto->IsNull()) {
433 lookup->NotFound();
434 return;
435 }
436
437 object = proto;
438 }
439}
440
441
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100442Object* CallICBase::TryCallAsFunction(Object* object) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000443 HandleScope scope;
444 Handle<Object> target(object);
445 Handle<Object> delegate = Execution::GetFunctionDelegate(target);
446
447 if (delegate->IsJSFunction()) {
448 // Patch the receiver and use the delegate as the function to
449 // invoke. This is used for invoking objects as if they were
450 // functions.
451 const int argc = this->target()->arguments_count();
452 StackFrameLocator locator;
453 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
454 int index = frame->ComputeExpressionsCount() - (argc + 1);
455 frame->SetExpression(index, *target);
456 }
457
458 return *delegate;
459}
460
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100461
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100462void CallICBase::ReceiverToObject(Handle<Object> object) {
Leon Clarkee46be812010-01-19 14:06:41 +0000463 HandleScope scope;
464 Handle<Object> receiver(object);
465
466 // Change the receiver to the result of calling ToObject on it.
467 const int argc = this->target()->arguments_count();
468 StackFrameLocator locator;
469 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
470 int index = frame->ComputeExpressionsCount() - (argc + 1);
471 frame->SetExpression(index, *Factory::ToObject(object));
472}
473
Steve Blocka7e24c12009-10-30 11:49:00 +0000474
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100475Object* CallICBase::LoadFunction(State state,
476 Handle<Object> object,
477 Handle<String> name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000478 // If the object is undefined or null it's illegal to try to get any
479 // of its properties; throw a TypeError in that case.
480 if (object->IsUndefined() || object->IsNull()) {
481 return TypeError("non_object_property_call", object, name);
482 }
483
Leon Clarkee46be812010-01-19 14:06:41 +0000484 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
485 ReceiverToObject(object);
486 }
487
Steve Blocka7e24c12009-10-30 11:49:00 +0000488 // Check if the name is trivially convertible to an index and get
489 // the element if so.
490 uint32_t index;
491 if (name->AsArrayIndex(&index)) {
492 Object* result = object->GetElement(index);
493 if (result->IsJSFunction()) return result;
494
495 // Try to find a suitable function delegate for the object at hand.
496 result = TryCallAsFunction(result);
497 if (result->IsJSFunction()) return result;
498
499 // Otherwise, it will fail in the lookup step.
500 }
501
502 // Lookup the property in the object.
503 LookupResult lookup;
504 LookupForRead(*object, *name, &lookup);
505
Andrei Popescu402d9372010-02-26 13:31:12 +0000506 if (!lookup.IsProperty()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000507 // If the object does not have the requested property, check which
508 // exception we need to throw.
Leon Clarkee46be812010-01-19 14:06:41 +0000509 if (IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000510 return ReferenceError("not_defined", name);
511 }
512 return TypeError("undefined_method", object, name);
513 }
514
515 // Lookup is valid: Update inline cache and stub cache.
Andrei Popescu31002712010-02-23 13:46:05 +0000516 if (FLAG_use_ic) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000517 UpdateCaches(&lookup, state, object, name);
518 }
519
520 // Get the property.
521 PropertyAttributes attr;
522 Object* result = object->GetProperty(*object, &lookup, *name, &attr);
523 if (result->IsFailure()) return result;
524 if (lookup.type() == INTERCEPTOR) {
525 // If the object does not have the requested property, check which
526 // exception we need to throw.
527 if (attr == ABSENT) {
Leon Clarkee46be812010-01-19 14:06:41 +0000528 if (IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000529 return ReferenceError("not_defined", name);
530 }
531 return TypeError("undefined_method", object, name);
532 }
533 }
534
535 ASSERT(result != Heap::the_hole_value());
536
537 if (result->IsJSFunction()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000538#ifdef ENABLE_DEBUGGER_SUPPORT
539 // Handle stepping into a function if step into is active.
540 if (Debug::StepInActive()) {
541 // Protect the result in a handle as the debugger can allocate and might
542 // cause GC.
543 HandleScope scope;
544 Handle<JSFunction> function(JSFunction::cast(result));
545 Debug::HandleStepIn(function, object, fp(), false);
546 return *function;
547 }
548#endif
549
550 return result;
551 }
552
553 // Try to find a suitable function delegate for the object at hand.
554 result = TryCallAsFunction(result);
555 return result->IsJSFunction() ?
556 result : TypeError("property_not_function", object, name);
557}
558
559
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100560void CallICBase::UpdateCaches(LookupResult* lookup,
Steve Block8defd9f2010-07-08 12:39:36 +0100561 State state,
562 Handle<Object> object,
563 Handle<String> name) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000564 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +0000565 if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000566
Steve Block8defd9f2010-07-08 12:39:36 +0100567 if (lookup->holder() != *object &&
568 HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) {
569 // Suppress optimization for prototype chains with slow properties objects
570 // in the middle.
571 return;
572 }
Steve Block8defd9f2010-07-08 12:39:36 +0100573
Steve Blocka7e24c12009-10-30 11:49:00 +0000574 // Compute the number of arguments.
575 int argc = target()->arguments_count();
576 InLoopFlag in_loop = target()->ic_in_loop();
577 Object* code = NULL;
578
579 if (state == UNINITIALIZED) {
580 // This is the first time we execute this inline cache.
581 // Set the target to the pre monomorphic stub to delay
582 // setting the monomorphic state.
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100583 code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000584 } else if (state == MONOMORPHIC) {
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100585 code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_);
Steve Blocka7e24c12009-10-30 11:49:00 +0000586 } else {
587 // Compute monomorphic stub.
588 switch (lookup->type()) {
589 case FIELD: {
590 int index = lookup->GetFieldIndex();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100591 code = StubCache::ComputeCallField(argc,
592 in_loop,
593 kind_,
594 *name,
595 *object,
596 lookup->holder(),
597 index);
Steve Blocka7e24c12009-10-30 11:49:00 +0000598 break;
599 }
600 case CONSTANT_FUNCTION: {
601 // Get the constant function and compute the code stub for this
602 // call; used for rewriting to monomorphic state and making sure
603 // that the code stub is in the stub cache.
604 JSFunction* function = lookup->GetConstantFunction();
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100605 code = StubCache::ComputeCallConstant(argc,
606 in_loop,
607 kind_,
608 *name,
609 *object,
610 lookup->holder(),
611 function);
Steve Blocka7e24c12009-10-30 11:49:00 +0000612 break;
613 }
614 case NORMAL: {
615 if (!object->IsJSObject()) return;
616 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
617
618 if (lookup->holder()->IsGlobalObject()) {
619 GlobalObject* global = GlobalObject::cast(lookup->holder());
620 JSGlobalPropertyCell* cell =
621 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
622 if (!cell->value()->IsJSFunction()) return;
623 JSFunction* function = JSFunction::cast(cell->value());
624 code = StubCache::ComputeCallGlobal(argc,
625 in_loop,
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100626 kind_,
Steve Blocka7e24c12009-10-30 11:49:00 +0000627 *name,
628 *receiver,
629 global,
630 cell,
631 function);
632 } else {
633 // There is only one shared stub for calling normalized
634 // properties. It does not traverse the prototype chain, so the
635 // property must be found in the receiver for the stub to be
636 // applicable.
637 if (lookup->holder() != *receiver) return;
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100638 code = StubCache::ComputeCallNormal(argc,
639 in_loop,
640 kind_,
641 *name,
642 *receiver);
Steve Blocka7e24c12009-10-30 11:49:00 +0000643 }
644 break;
645 }
646 case INTERCEPTOR: {
647 ASSERT(HasInterceptorGetter(lookup->holder()));
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100648 code = StubCache::ComputeCallInterceptor(argc,
649 kind_,
650 *name,
651 *object,
Steve Blocka7e24c12009-10-30 11:49:00 +0000652 lookup->holder());
653 break;
654 }
655 default:
656 return;
657 }
658 }
659
660 // If we're unable to compute the stub (not enough memory left), we
661 // simply avoid updating the caches.
662 if (code == NULL || code->IsFailure()) return;
663
664 // Patch the call site depending on the state of the cache.
665 if (state == UNINITIALIZED ||
666 state == PREMONOMORPHIC ||
667 state == MONOMORPHIC ||
668 state == MONOMORPHIC_PROTOTYPE_FAILURE) {
669 set_target(Code::cast(code));
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100670 } else if (state == MEGAMORPHIC) {
Steve Block8defd9f2010-07-08 12:39:36 +0100671 // Cache code holding map should be consistent with
672 // GenerateMonomorphicCacheProbe. It is not the map which holds the stub.
673 Map* map = JSObject::cast(object->IsJSObject() ? *object :
674 object->GetPrototype())->map();
675
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100676 // Update the stub cache.
Steve Block8defd9f2010-07-08 12:39:36 +0100677 StubCache::Set(*name, map, Code::cast(code));
Steve Blocka7e24c12009-10-30 11:49:00 +0000678 }
679
680#ifdef DEBUG
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100681 TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
682 name, state, target(), in_loop ? " (in-loop)" : "");
Steve Blocka7e24c12009-10-30 11:49:00 +0000683#endif
684}
685
686
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +0100687Object* KeyedCallIC::LoadFunction(State state,
688 Handle<Object> object,
689 Handle<Object> key) {
690 if (key->IsSymbol()) {
691 return CallICBase::LoadFunction(state, object, Handle<String>::cast(key));
692 }
693
694 if (object->IsUndefined() || object->IsNull()) {
695 return TypeError("non_object_property_call", object, key);
696 }
697
698 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
699 ReceiverToObject(object);
700 }
701
702 if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
703 int argc = target()->arguments_count();
704 InLoopFlag in_loop = target()->ic_in_loop();
705 Object* code = StubCache::ComputeCallMegamorphic(
706 argc, in_loop, Code::KEYED_CALL_IC);
707 if (!code->IsFailure()) {
708 set_target(Code::cast(code));
709#ifdef DEBUG
710 TraceIC(
711 "KeyedCallIC", key, state, target(), in_loop ? " (in-loop)" : "");
712#endif
713 }
714 }
715 Object* result = Runtime::GetObjectProperty(object, key);
716 if (result->IsJSFunction()) return result;
717 result = TryCallAsFunction(result);
718 return result->IsJSFunction() ?
719 result : TypeError("property_not_function", object, key);
720}
721
722
Steve Blocka7e24c12009-10-30 11:49:00 +0000723Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
724 // If the object is undefined or null it's illegal to try to get any
725 // of its properties; throw a TypeError in that case.
726 if (object->IsUndefined() || object->IsNull()) {
727 return TypeError("non_object_property_load", object, name);
728 }
729
730 if (FLAG_use_ic) {
731 // Use specialized code for getting the length of strings and
732 // string wrapper objects. The length property of string wrapper
733 // objects is read-only and therefore always returns the length of
734 // the underlying string value. See ECMA-262 15.5.5.1.
735 if ((object->IsString() || object->IsStringWrapper()) &&
736 name->Equals(Heap::length_symbol())) {
737 HandleScope scope;
738 // Get the string if we have a string wrapper object.
739 if (object->IsJSValue()) {
740 object = Handle<Object>(Handle<JSValue>::cast(object)->value());
741 }
742#ifdef DEBUG
743 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n");
744#endif
Kristian Monsen25f61362010-05-21 11:50:48 +0100745 Map* map = HeapObject::cast(*object)->map();
746 if (object->IsString()) {
747 const int offset = String::kLengthOffset;
748 PatchInlinedLoad(address(), map, offset);
749 }
750
Steve Blocka7e24c12009-10-30 11:49:00 +0000751 Code* target = NULL;
752 target = Builtins::builtin(Builtins::LoadIC_StringLength);
753 set_target(target);
Steve Blocka7e24c12009-10-30 11:49:00 +0000754 return Smi::FromInt(String::cast(*object)->length());
755 }
756
757 // Use specialized code for getting the length of arrays.
758 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
759#ifdef DEBUG
760 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
761#endif
Kristian Monsen25f61362010-05-21 11:50:48 +0100762 Map* map = HeapObject::cast(*object)->map();
763 const int offset = JSArray::kLengthOffset;
764 PatchInlinedLoad(address(), map, offset);
765
Steve Blocka7e24c12009-10-30 11:49:00 +0000766 Code* target = Builtins::builtin(Builtins::LoadIC_ArrayLength);
767 set_target(target);
Steve Blocka7e24c12009-10-30 11:49:00 +0000768 return JSArray::cast(*object)->length();
769 }
770
771 // Use specialized code for getting prototype of functions.
Steve Block6ded16b2010-05-10 14:33:55 +0100772 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
773 JSFunction::cast(*object)->should_have_prototype()) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000774#ifdef DEBUG
775 if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
776#endif
777 Code* target = Builtins::builtin(Builtins::LoadIC_FunctionPrototype);
778 set_target(target);
Steve Blocka7e24c12009-10-30 11:49:00 +0000779 return Accessors::FunctionGetPrototype(*object, 0);
780 }
781 }
782
783 // Check if the name is trivially convertible to an index and get
784 // the element if so.
785 uint32_t index;
786 if (name->AsArrayIndex(&index)) return object->GetElement(index);
787
788 // Named lookup in the object.
789 LookupResult lookup;
790 LookupForRead(*object, *name, &lookup);
791
Andrei Popescu402d9372010-02-26 13:31:12 +0000792 // If we did not find a property, check if we need to throw an exception.
793 if (!lookup.IsProperty()) {
Leon Clarkee46be812010-01-19 14:06:41 +0000794 if (FLAG_strict || IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000795 return ReferenceError("not_defined", name);
796 }
797 LOG(SuspectReadEvent(*name, *object));
798 }
799
800 bool can_be_inlined =
801 FLAG_use_ic &&
802 state == PREMONOMORPHIC &&
Andrei Popescu402d9372010-02-26 13:31:12 +0000803 lookup.IsProperty() &&
Steve Blocka7e24c12009-10-30 11:49:00 +0000804 lookup.IsCacheable() &&
805 lookup.holder() == *object &&
806 lookup.type() == FIELD &&
807 !object->IsAccessCheckNeeded();
808
809 if (can_be_inlined) {
810 Map* map = lookup.holder()->map();
811 // Property's index in the properties array. If negative we have
812 // an inobject property.
813 int index = lookup.GetFieldIndex() - map->inobject_properties();
814 if (index < 0) {
815 // Index is an offset from the end of the object.
816 int offset = map->instance_size() + (index * kPointerSize);
817 if (PatchInlinedLoad(address(), map, offset)) {
818 set_target(megamorphic_stub());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100819#ifdef DEBUG
820 if (FLAG_trace_ic) {
821 PrintF("[LoadIC : inline patch %s]\n", *name->ToCString());
822 }
Kristian Monsen50ef84f2010-07-29 15:18:00 +0100823#endif
824 return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
825#ifdef DEBUG
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100826 } else {
827 if (FLAG_trace_ic) {
828 PrintF("[LoadIC : no inline patch %s (patching failed)]\n",
829 *name->ToCString());
830 }
831 }
832 } else {
833 if (FLAG_trace_ic) {
834 PrintF("[LoadIC : no inline patch %s (not inobject)]\n",
835 *name->ToCString());
836 }
837 }
838 } else {
839 if (FLAG_use_ic && state == PREMONOMORPHIC) {
840 if (FLAG_trace_ic) {
841 PrintF("[LoadIC : no inline patch %s (not inlinable)]\n",
842 *name->ToCString());
843#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000844 }
845 }
846 }
847
848 // Update inline cache and stub cache.
Andrei Popescu31002712010-02-23 13:46:05 +0000849 if (FLAG_use_ic) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000850 UpdateCaches(&lookup, state, object, name);
851 }
852
853 PropertyAttributes attr;
Andrei Popescu402d9372010-02-26 13:31:12 +0000854 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000855 // Get the property.
856 Object* result = object->GetProperty(*object, &lookup, *name, &attr);
857 if (result->IsFailure()) return result;
858 // If the property is not present, check if we need to throw an
859 // exception.
Leon Clarkee46be812010-01-19 14:06:41 +0000860 if (attr == ABSENT && IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000861 return ReferenceError("not_defined", name);
862 }
863 return result;
864 }
865
866 // Get the property.
867 return object->GetProperty(*object, &lookup, *name, &attr);
868}
869
870
871void LoadIC::UpdateCaches(LookupResult* lookup,
872 State state,
873 Handle<Object> object,
874 Handle<String> name) {
Steve Block6ded16b2010-05-10 14:33:55 +0100875 // Bail out if the result is not cacheable.
876 if (!lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000877
878 // Loading properties from values is not common, so don't try to
879 // deal with non-JS objects here.
880 if (!object->IsJSObject()) return;
881 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
882
Steve Block8defd9f2010-07-08 12:39:36 +0100883 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
884
Steve Blocka7e24c12009-10-30 11:49:00 +0000885 // Compute the code stub for this load.
886 Object* code = NULL;
887 if (state == UNINITIALIZED) {
888 // This is the first time we execute this inline cache.
889 // Set the target to the pre monomorphic stub to delay
890 // setting the monomorphic state.
891 code = pre_monomorphic_stub();
Steve Block6ded16b2010-05-10 14:33:55 +0100892 } else if (!lookup->IsProperty()) {
893 // Nonexistent property. The result is undefined.
894 code = StubCache::ComputeLoadNonexistent(*name, *receiver);
Steve Blocka7e24c12009-10-30 11:49:00 +0000895 } else {
896 // Compute monomorphic stub.
897 switch (lookup->type()) {
898 case FIELD: {
899 code = StubCache::ComputeLoadField(*name, *receiver,
900 lookup->holder(),
901 lookup->GetFieldIndex());
902 break;
903 }
904 case CONSTANT_FUNCTION: {
905 Object* constant = lookup->GetConstantFunction();
906 code = StubCache::ComputeLoadConstant(*name, *receiver,
907 lookup->holder(), constant);
908 break;
909 }
910 case NORMAL: {
911 if (lookup->holder()->IsGlobalObject()) {
912 GlobalObject* global = GlobalObject::cast(lookup->holder());
913 JSGlobalPropertyCell* cell =
914 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
915 code = StubCache::ComputeLoadGlobal(*name,
916 *receiver,
917 global,
918 cell,
919 lookup->IsDontDelete());
920 } else {
921 // There is only one shared stub for loading normalized
922 // properties. It does not traverse the prototype chain, so the
923 // property must be found in the receiver for the stub to be
924 // applicable.
925 if (lookup->holder() != *receiver) return;
Steve Block8defd9f2010-07-08 12:39:36 +0100926 code = StubCache::ComputeLoadNormal();
Steve Blocka7e24c12009-10-30 11:49:00 +0000927 }
928 break;
929 }
930 case CALLBACKS: {
931 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
932 AccessorInfo* callback =
933 AccessorInfo::cast(lookup->GetCallbackObject());
934 if (v8::ToCData<Address>(callback->getter()) == 0) return;
935 code = StubCache::ComputeLoadCallback(*name, *receiver,
936 lookup->holder(), callback);
937 break;
938 }
939 case INTERCEPTOR: {
940 ASSERT(HasInterceptorGetter(lookup->holder()));
941 code = StubCache::ComputeLoadInterceptor(*name, *receiver,
942 lookup->holder());
943 break;
944 }
945 default:
946 return;
947 }
948 }
949
950 // If we're unable to compute the stub (not enough memory left), we
951 // simply avoid updating the caches.
952 if (code == NULL || code->IsFailure()) return;
953
954 // Patch the call site depending on the state of the cache.
955 if (state == UNINITIALIZED || state == PREMONOMORPHIC ||
956 state == MONOMORPHIC_PROTOTYPE_FAILURE) {
957 set_target(Code::cast(code));
958 } else if (state == MONOMORPHIC) {
959 set_target(megamorphic_stub());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +0100960 } else if (state == MEGAMORPHIC) {
Steve Block8defd9f2010-07-08 12:39:36 +0100961 // Cache code holding map should be consistent with
962 // GenerateMonomorphicCacheProbe.
963 Map* map = JSObject::cast(object->IsJSObject() ? *object :
964 object->GetPrototype())->map();
965
966 StubCache::Set(*name, map, Code::cast(code));
Steve Blocka7e24c12009-10-30 11:49:00 +0000967 }
968
969#ifdef DEBUG
970 TraceIC("LoadIC", name, state, target());
971#endif
972}
973
974
975Object* KeyedLoadIC::Load(State state,
976 Handle<Object> object,
977 Handle<Object> key) {
978 if (key->IsSymbol()) {
979 Handle<String> name = Handle<String>::cast(key);
980
981 // If the object is undefined or null it's illegal to try to get any
982 // of its properties; throw a TypeError in that case.
983 if (object->IsUndefined() || object->IsNull()) {
984 return TypeError("non_object_property_load", object, name);
985 }
986
987 if (FLAG_use_ic) {
988 // Use specialized code for getting the length of strings.
989 if (object->IsString() && name->Equals(Heap::length_symbol())) {
990 Handle<String> string = Handle<String>::cast(object);
991 Object* code = NULL;
992 code = StubCache::ComputeKeyedLoadStringLength(*name, *string);
993 if (code->IsFailure()) return code;
994 set_target(Code::cast(code));
995#ifdef DEBUG
996 TraceIC("KeyedLoadIC", name, state, target());
997#endif // DEBUG
998 return Smi::FromInt(string->length());
999 }
1000
1001 // Use specialized code for getting the length of arrays.
1002 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
1003 Handle<JSArray> array = Handle<JSArray>::cast(object);
1004 Object* code = StubCache::ComputeKeyedLoadArrayLength(*name, *array);
1005 if (code->IsFailure()) return code;
1006 set_target(Code::cast(code));
1007#ifdef DEBUG
1008 TraceIC("KeyedLoadIC", name, state, target());
1009#endif // DEBUG
1010 return JSArray::cast(*object)->length();
1011 }
1012
1013 // Use specialized code for getting prototype of functions.
Steve Block6ded16b2010-05-10 14:33:55 +01001014 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
1015 JSFunction::cast(*object)->should_have_prototype()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001016 Handle<JSFunction> function = Handle<JSFunction>::cast(object);
1017 Object* code =
1018 StubCache::ComputeKeyedLoadFunctionPrototype(*name, *function);
1019 if (code->IsFailure()) return code;
1020 set_target(Code::cast(code));
1021#ifdef DEBUG
1022 TraceIC("KeyedLoadIC", name, state, target());
1023#endif // DEBUG
1024 return Accessors::FunctionGetPrototype(*object, 0);
1025 }
1026 }
1027
1028 // Check if the name is trivially convertible to an index and get
1029 // the element or char if so.
1030 uint32_t index = 0;
1031 if (name->AsArrayIndex(&index)) {
1032 HandleScope scope;
1033 // Rewrite to the generic keyed load stub.
1034 if (FLAG_use_ic) set_target(generic_stub());
1035 return Runtime::GetElementOrCharAt(object, index);
1036 }
1037
1038 // Named lookup.
1039 LookupResult lookup;
1040 LookupForRead(*object, *name, &lookup);
1041
Andrei Popescu402d9372010-02-26 13:31:12 +00001042 // If we did not find a property, check if we need to throw an exception.
1043 if (!lookup.IsProperty()) {
Leon Clarkee46be812010-01-19 14:06:41 +00001044 if (FLAG_strict || IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001045 return ReferenceError("not_defined", name);
1046 }
1047 }
1048
Andrei Popescu31002712010-02-23 13:46:05 +00001049 if (FLAG_use_ic) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001050 UpdateCaches(&lookup, state, object, name);
1051 }
1052
1053 PropertyAttributes attr;
Andrei Popescu402d9372010-02-26 13:31:12 +00001054 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001055 // Get the property.
1056 Object* result = object->GetProperty(*object, &lookup, *name, &attr);
1057 if (result->IsFailure()) return result;
1058 // If the property is not present, check if we need to throw an
1059 // exception.
Leon Clarkee46be812010-01-19 14:06:41 +00001060 if (attr == ABSENT && IsContextual(object)) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001061 return ReferenceError("not_defined", name);
1062 }
1063 return result;
1064 }
1065
1066 return object->GetProperty(*object, &lookup, *name, &attr);
1067 }
1068
1069 // Do not use ICs for objects that require access checks (including
1070 // the global object).
1071 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
1072
1073 if (use_ic) {
Steve Block3ce2e202009-11-05 08:53:23 +00001074 Code* stub = generic_stub();
Leon Clarkee46be812010-01-19 14:06:41 +00001075 if (object->IsString() && key->IsNumber()) {
1076 stub = string_stub();
1077 } else if (object->IsJSObject()) {
Steve Block3ce2e202009-11-05 08:53:23 +00001078 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1079 if (receiver->HasExternalArrayElements()) {
1080 stub = external_array_stub(receiver->GetElementsKind());
Andrei Popescu402d9372010-02-26 13:31:12 +00001081 } else if (receiver->HasIndexedInterceptor()) {
1082 stub = indexed_interceptor_stub();
Steve Block3ce2e202009-11-05 08:53:23 +00001083 }
1084 }
1085 set_target(stub);
Steve Block8defd9f2010-07-08 12:39:36 +01001086 // For JSObjects with fast elements that are not value wrappers
1087 // and that do not have indexed interceptors, we initialize the
1088 // inlined fast case (if present) by patching the inlined map
1089 // check.
Steve Blocka7e24c12009-10-30 11:49:00 +00001090 if (object->IsJSObject() &&
1091 !object->IsJSValue() &&
Steve Block8defd9f2010-07-08 12:39:36 +01001092 !JSObject::cast(*object)->HasIndexedInterceptor() &&
1093 JSObject::cast(*object)->HasFastElements()) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001094 Map* map = JSObject::cast(*object)->map();
1095 PatchInlinedLoad(address(), map);
1096 }
1097 }
1098
1099 // Get the property.
1100 return Runtime::GetObjectProperty(object, key);
1101}
1102
1103
1104void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
1105 Handle<Object> object, Handle<String> name) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001106 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +00001107 if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001108
1109 if (!object->IsJSObject()) return;
1110 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1111
Steve Block8defd9f2010-07-08 12:39:36 +01001112 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
1113
Steve Blocka7e24c12009-10-30 11:49:00 +00001114 // Compute the code stub for this load.
1115 Object* code = NULL;
1116
1117 if (state == UNINITIALIZED) {
1118 // This is the first time we execute this inline cache.
1119 // Set the target to the pre monomorphic stub to delay
1120 // setting the monomorphic state.
1121 code = pre_monomorphic_stub();
1122 } else {
1123 // Compute a monomorphic stub.
1124 switch (lookup->type()) {
1125 case FIELD: {
1126 code = StubCache::ComputeKeyedLoadField(*name, *receiver,
1127 lookup->holder(),
1128 lookup->GetFieldIndex());
1129 break;
1130 }
1131 case CONSTANT_FUNCTION: {
1132 Object* constant = lookup->GetConstantFunction();
1133 code = StubCache::ComputeKeyedLoadConstant(*name, *receiver,
1134 lookup->holder(), constant);
1135 break;
1136 }
1137 case CALLBACKS: {
1138 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
1139 AccessorInfo* callback =
1140 AccessorInfo::cast(lookup->GetCallbackObject());
1141 if (v8::ToCData<Address>(callback->getter()) == 0) return;
1142 code = StubCache::ComputeKeyedLoadCallback(*name, *receiver,
1143 lookup->holder(), callback);
1144 break;
1145 }
1146 case INTERCEPTOR: {
1147 ASSERT(HasInterceptorGetter(lookup->holder()));
1148 code = StubCache::ComputeKeyedLoadInterceptor(*name, *receiver,
1149 lookup->holder());
1150 break;
1151 }
1152 default: {
1153 // Always rewrite to the generic case so that we do not
1154 // repeatedly try to rewrite.
1155 code = generic_stub();
1156 break;
1157 }
1158 }
1159 }
1160
1161 // If we're unable to compute the stub (not enough memory left), we
1162 // simply avoid updating the caches.
1163 if (code == NULL || code->IsFailure()) return;
1164
1165 // Patch the call site depending on the state of the cache. Make
1166 // sure to always rewrite from monomorphic to megamorphic.
1167 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
1168 if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
1169 set_target(Code::cast(code));
1170 } else if (state == MONOMORPHIC) {
1171 set_target(megamorphic_stub());
1172 }
1173
1174#ifdef DEBUG
1175 TraceIC("KeyedLoadIC", name, state, target());
1176#endif
1177}
1178
1179
1180static bool StoreICableLookup(LookupResult* lookup) {
1181 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +00001182 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +00001183
1184 // If the property is read-only, we leave the IC in its current
1185 // state.
1186 if (lookup->IsReadOnly()) return false;
1187
Steve Blocka7e24c12009-10-30 11:49:00 +00001188 return true;
1189}
1190
1191
1192static bool LookupForWrite(JSObject* object,
1193 String* name,
1194 LookupResult* lookup) {
1195 object->LocalLookup(name, lookup);
1196 if (!StoreICableLookup(lookup)) {
1197 return false;
1198 }
1199
1200 if (lookup->type() == INTERCEPTOR) {
1201 if (object->GetNamedInterceptor()->setter()->IsUndefined()) {
1202 object->LocalLookupRealNamedProperty(name, lookup);
1203 return StoreICableLookup(lookup);
1204 }
1205 }
1206
1207 return true;
1208}
1209
1210
1211Object* StoreIC::Store(State state,
1212 Handle<Object> object,
1213 Handle<String> name,
1214 Handle<Object> value) {
1215 // If the object is undefined or null it's illegal to try to set any
1216 // properties on it; throw a TypeError in that case.
1217 if (object->IsUndefined() || object->IsNull()) {
1218 return TypeError("non_object_property_store", object, name);
1219 }
1220
1221 // Ignore stores where the receiver is not a JSObject.
1222 if (!object->IsJSObject()) return *value;
1223 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1224
1225 // Check if the given name is an array index.
1226 uint32_t index;
1227 if (name->AsArrayIndex(&index)) {
1228 HandleScope scope;
1229 Handle<Object> result = SetElement(receiver, index, value);
1230 if (result.is_null()) return Failure::Exception();
1231 return *value;
1232 }
1233
Steve Block6ded16b2010-05-10 14:33:55 +01001234 // Use specialized code for setting the length of arrays.
1235 if (receiver->IsJSArray()
1236 && name->Equals(Heap::length_symbol())
1237 && receiver->AllowsSetElementsLength()) {
1238#ifdef DEBUG
1239 if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n");
1240#endif
1241 Code* target = Builtins::builtin(Builtins::StoreIC_ArrayLength);
1242 set_target(target);
Steve Block6ded16b2010-05-10 14:33:55 +01001243 return receiver->SetProperty(*name, *value, NONE);
1244 }
1245
Steve Blocka7e24c12009-10-30 11:49:00 +00001246 // Lookup the property locally in the receiver.
1247 if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
1248 LookupResult lookup;
Kristian Monsen50ef84f2010-07-29 15:18:00 +01001249
Steve Blocka7e24c12009-10-30 11:49:00 +00001250 if (LookupForWrite(*receiver, *name, &lookup)) {
Kristian Monsen50ef84f2010-07-29 15:18:00 +01001251 bool can_be_inlined =
1252 state == UNINITIALIZED &&
1253 lookup.IsProperty() &&
1254 lookup.holder() == *receiver &&
1255 lookup.type() == FIELD &&
1256 !receiver->IsAccessCheckNeeded();
1257
1258 if (can_be_inlined) {
1259 Map* map = lookup.holder()->map();
1260 // Property's index in the properties array. If negative we have
1261 // an inobject property.
1262 int index = lookup.GetFieldIndex() - map->inobject_properties();
1263 if (index < 0) {
1264 // Index is an offset from the end of the object.
1265 int offset = map->instance_size() + (index * kPointerSize);
1266 if (PatchInlinedStore(address(), map, offset)) {
1267 set_target(megamorphic_stub());
1268#ifdef DEBUG
1269 if (FLAG_trace_ic) {
1270 PrintF("[StoreIC : inline patch %s]\n", *name->ToCString());
1271 }
1272#endif
1273 return receiver->SetProperty(*name, *value, NONE);
1274#ifdef DEBUG
1275
1276 } else {
1277 if (FLAG_trace_ic) {
1278 PrintF("[StoreIC : no inline patch %s (patching failed)]\n",
1279 *name->ToCString());
1280 }
1281 }
1282 } else {
1283 if (FLAG_trace_ic) {
1284 PrintF("[StoreIC : no inline patch %s (not inobject)]\n",
1285 *name->ToCString());
1286 }
1287 }
1288 } else {
1289 if (state == PREMONOMORPHIC) {
1290 if (FLAG_trace_ic) {
1291 PrintF("[StoreIC : no inline patch %s (not inlinable)]\n",
1292 *name->ToCString());
1293#endif
1294 }
1295 }
1296 }
1297
1298 // If no inlined store ic was patched, generate a stub for this
1299 // store.
Steve Blocka7e24c12009-10-30 11:49:00 +00001300 UpdateCaches(&lookup, state, receiver, name, value);
1301 }
1302 }
1303
1304 // Set the property.
1305 return receiver->SetProperty(*name, *value, NONE);
1306}
1307
1308
1309void StoreIC::UpdateCaches(LookupResult* lookup,
1310 State state,
1311 Handle<JSObject> receiver,
1312 Handle<String> name,
1313 Handle<Object> value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001314 // Skip JSGlobalProxy.
1315 ASSERT(!receiver->IsJSGlobalProxy());
1316
1317 ASSERT(StoreICableLookup(lookup));
1318
1319 // If the property has a non-field type allowing map transitions
1320 // where there is extra room in the object, we leave the IC in its
1321 // current state.
1322 PropertyType type = lookup->type();
1323
1324 // Compute the code stub for this store; used for rewriting to
1325 // monomorphic state and making sure that the code stub is in the
1326 // stub cache.
1327 Object* code = NULL;
1328 switch (type) {
1329 case FIELD: {
1330 code = StubCache::ComputeStoreField(*name, *receiver,
1331 lookup->GetFieldIndex());
1332 break;
1333 }
1334 case MAP_TRANSITION: {
1335 if (lookup->GetAttributes() != NONE) return;
1336 HandleScope scope;
1337 ASSERT(type == MAP_TRANSITION);
1338 Handle<Map> transition(lookup->GetTransitionMap());
1339 int index = transition->PropertyIndexFor(*name);
1340 code = StubCache::ComputeStoreField(*name, *receiver, index, *transition);
1341 break;
1342 }
1343 case NORMAL: {
Steve Block8defd9f2010-07-08 12:39:36 +01001344 if (receiver->IsGlobalObject()) {
1345 // The stub generated for the global object picks the value directly
1346 // from the property cell. So the property must be directly on the
1347 // global object.
1348 Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
1349 JSGlobalPropertyCell* cell =
1350 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
1351 code = StubCache::ComputeStoreGlobal(*name, *global, cell);
1352 } else {
1353 if (lookup->holder() != *receiver) return;
1354 code = StubCache::ComputeStoreNormal();
Steve Blocka7e24c12009-10-30 11:49:00 +00001355 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001356 break;
1357 }
1358 case CALLBACKS: {
1359 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
1360 AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
1361 if (v8::ToCData<Address>(callback->setter()) == 0) return;
1362 code = StubCache::ComputeStoreCallback(*name, *receiver, callback);
1363 break;
1364 }
1365 case INTERCEPTOR: {
1366 ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined());
1367 code = StubCache::ComputeStoreInterceptor(*name, *receiver);
1368 break;
1369 }
1370 default:
1371 return;
1372 }
1373
1374 // If we're unable to compute the stub (not enough memory left), we
1375 // simply avoid updating the caches.
1376 if (code == NULL || code->IsFailure()) return;
1377
1378 // Patch the call site depending on the state of the cache.
1379 if (state == UNINITIALIZED || state == MONOMORPHIC_PROTOTYPE_FAILURE) {
1380 set_target(Code::cast(code));
1381 } else if (state == MONOMORPHIC) {
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01001382 // Only move to megamorphic if the target changes.
Steve Blocka7e24c12009-10-30 11:49:00 +00001383 if (target() != Code::cast(code)) set_target(megamorphic_stub());
Kristian Monsen9dcf7e22010-06-28 14:14:28 +01001384 } else if (state == MEGAMORPHIC) {
1385 // Update the stub cache.
1386 StubCache::Set(*name, receiver->map(), Code::cast(code));
Steve Blocka7e24c12009-10-30 11:49:00 +00001387 }
1388
1389#ifdef DEBUG
1390 TraceIC("StoreIC", name, state, target());
1391#endif
1392}
1393
1394
1395Object* KeyedStoreIC::Store(State state,
1396 Handle<Object> object,
1397 Handle<Object> key,
1398 Handle<Object> value) {
1399 if (key->IsSymbol()) {
1400 Handle<String> name = Handle<String>::cast(key);
1401
1402 // If the object is undefined or null it's illegal to try to set any
1403 // properties on it; throw a TypeError in that case.
1404 if (object->IsUndefined() || object->IsNull()) {
1405 return TypeError("non_object_property_store", object, name);
1406 }
1407
1408 // Ignore stores where the receiver is not a JSObject.
1409 if (!object->IsJSObject()) return *value;
1410 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1411
1412 // Check if the given name is an array index.
1413 uint32_t index;
1414 if (name->AsArrayIndex(&index)) {
1415 HandleScope scope;
1416 Handle<Object> result = SetElement(receiver, index, value);
1417 if (result.is_null()) return Failure::Exception();
1418 return *value;
1419 }
1420
1421 // Lookup the property locally in the receiver.
1422 LookupResult lookup;
1423 receiver->LocalLookup(*name, &lookup);
1424
1425 // Update inline cache and stub cache.
Andrei Popescu31002712010-02-23 13:46:05 +00001426 if (FLAG_use_ic) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001427 UpdateCaches(&lookup, state, receiver, name, value);
1428 }
1429
1430 // Set the property.
1431 return receiver->SetProperty(*name, *value, NONE);
1432 }
1433
1434 // Do not use ICs for objects that require access checks (including
1435 // the global object).
1436 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
1437 ASSERT(!(use_ic && object->IsJSGlobalProxy()));
1438
Steve Block3ce2e202009-11-05 08:53:23 +00001439 if (use_ic) {
1440 Code* stub = generic_stub();
1441 if (object->IsJSObject()) {
1442 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1443 if (receiver->HasExternalArrayElements()) {
1444 stub = external_array_stub(receiver->GetElementsKind());
1445 }
1446 }
1447 set_target(stub);
1448 }
Steve Blocka7e24c12009-10-30 11:49:00 +00001449
1450 // Set the property.
1451 return Runtime::SetObjectProperty(object, key, value, NONE);
1452}
1453
1454
1455void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
1456 State state,
1457 Handle<JSObject> receiver,
1458 Handle<String> name,
1459 Handle<Object> value) {
Steve Blocka7e24c12009-10-30 11:49:00 +00001460 // Skip JSGlobalProxy.
1461 if (receiver->IsJSGlobalProxy()) return;
1462
1463 // Bail out if we didn't find a result.
Andrei Popescu402d9372010-02-26 13:31:12 +00001464 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return;
Steve Blocka7e24c12009-10-30 11:49:00 +00001465
1466 // If the property is read-only, we leave the IC in its current
1467 // state.
1468 if (lookup->IsReadOnly()) return;
1469
1470 // If the property has a non-field type allowing map transitions
1471 // where there is extra room in the object, we leave the IC in its
1472 // current state.
1473 PropertyType type = lookup->type();
1474
1475 // Compute the code stub for this store; used for rewriting to
1476 // monomorphic state and making sure that the code stub is in the
1477 // stub cache.
1478 Object* code = NULL;
1479
1480 switch (type) {
1481 case FIELD: {
1482 code = StubCache::ComputeKeyedStoreField(*name, *receiver,
1483 lookup->GetFieldIndex());
1484 break;
1485 }
1486 case MAP_TRANSITION: {
1487 if (lookup->GetAttributes() == NONE) {
1488 HandleScope scope;
1489 ASSERT(type == MAP_TRANSITION);
1490 Handle<Map> transition(lookup->GetTransitionMap());
1491 int index = transition->PropertyIndexFor(*name);
1492 code = StubCache::ComputeKeyedStoreField(*name, *receiver,
1493 index, *transition);
1494 break;
1495 }
1496 // fall through.
1497 }
1498 default: {
1499 // Always rewrite to the generic case so that we do not
1500 // repeatedly try to rewrite.
1501 code = generic_stub();
1502 break;
1503 }
1504 }
1505
1506 // If we're unable to compute the stub (not enough memory left), we
1507 // simply avoid updating the caches.
1508 if (code == NULL || code->IsFailure()) return;
1509
1510 // Patch the call site depending on the state of the cache. Make
1511 // sure to always rewrite from monomorphic to megamorphic.
1512 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
1513 if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
1514 set_target(Code::cast(code));
1515 } else if (state == MONOMORPHIC) {
1516 set_target(megamorphic_stub());
1517 }
1518
1519#ifdef DEBUG
1520 TraceIC("KeyedStoreIC", name, state, target());
1521#endif
1522}
1523
1524
1525// ----------------------------------------------------------------------------
1526// Static IC stub generators.
1527//
1528
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001529static Object* CompileFunction(Object* result,
1530 Handle<Object> object,
1531 InLoopFlag in_loop) {
1532 // Compile now with optimization.
1533 HandleScope scope;
1534 Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
1535 if (in_loop == IN_LOOP) {
1536 CompileLazyInLoop(function, object, CLEAR_EXCEPTION);
1537 } else {
1538 CompileLazy(function, object, CLEAR_EXCEPTION);
1539 }
1540 return *function;
1541}
1542
1543
1544// Used from ic-<arch>.cc.
Steve Blocka7e24c12009-10-30 11:49:00 +00001545Object* CallIC_Miss(Arguments args) {
1546 NoHandleAllocation na;
1547 ASSERT(args.length() == 2);
1548 CallIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001549 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001550 Object* result =
1551 ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
1552
1553 // The first time the inline cache is updated may be the first time the
1554 // function it references gets called. If the function was lazily compiled
1555 // then the first call will trigger a compilation. We check for this case
1556 // and we do the compilation immediately, instead of waiting for the stub
1557 // currently attached to the JSFunction object to trigger compilation. We
1558 // do this in the case where we know that the inline cache is inside a loop,
1559 // because then we know that we want to optimize the function.
1560 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
1561 return result;
1562 }
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001563 return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop());
Steve Blocka7e24c12009-10-30 11:49:00 +00001564}
1565
1566
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001567// Used from ic-<arch>.cc.
1568Object* KeyedCallIC_Miss(Arguments args) {
1569 NoHandleAllocation na;
1570 ASSERT(args.length() == 2);
1571 KeyedCallIC ic;
1572 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
1573 Object* result =
1574 ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1));
1575
1576 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
1577 return result;
1578 }
1579 return CompileFunction(result, args.at<Object>(0), ic.target()->ic_in_loop());
1580}
1581
1582
1583// Used from ic-<arch>.cc.
Steve Blocka7e24c12009-10-30 11:49:00 +00001584Object* LoadIC_Miss(Arguments args) {
1585 NoHandleAllocation na;
1586 ASSERT(args.length() == 2);
1587 LoadIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001588 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001589 return ic.Load(state, args.at<Object>(0), args.at<String>(1));
1590}
1591
1592
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001593// Used from ic-<arch>.cc
Steve Blocka7e24c12009-10-30 11:49:00 +00001594Object* KeyedLoadIC_Miss(Arguments args) {
1595 NoHandleAllocation na;
1596 ASSERT(args.length() == 2);
1597 KeyedLoadIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001598 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001599 return ic.Load(state, args.at<Object>(0), args.at<Object>(1));
1600}
1601
1602
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001603// Used from ic-<arch>.cc.
Steve Blocka7e24c12009-10-30 11:49:00 +00001604Object* StoreIC_Miss(Arguments args) {
1605 NoHandleAllocation na;
1606 ASSERT(args.length() == 3);
1607 StoreIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001608 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001609 return ic.Store(state, args.at<Object>(0), args.at<String>(1),
1610 args.at<Object>(2));
1611}
1612
1613
Steve Block6ded16b2010-05-10 14:33:55 +01001614Object* StoreIC_ArrayLength(Arguments args) {
1615 NoHandleAllocation nha;
1616
1617 ASSERT(args.length() == 2);
1618 JSObject* receiver = JSObject::cast(args[0]);
1619 Object* len = args[1];
1620
1621 Object* result = receiver->SetElementsLength(len);
1622 if (result->IsFailure()) return result;
1623 return len;
1624}
1625
1626
Steve Blocka7e24c12009-10-30 11:49:00 +00001627// Extend storage is called in a store inline cache when
1628// it is necessary to extend the properties array of a
1629// JSObject.
1630Object* SharedStoreIC_ExtendStorage(Arguments args) {
1631 NoHandleAllocation na;
1632 ASSERT(args.length() == 3);
1633
1634 // Convert the parameters
1635 JSObject* object = JSObject::cast(args[0]);
1636 Map* transition = Map::cast(args[1]);
1637 Object* value = args[2];
1638
1639 // Check the object has run out out property space.
1640 ASSERT(object->HasFastProperties());
1641 ASSERT(object->map()->unused_property_fields() == 0);
1642
1643 // Expand the properties array.
1644 FixedArray* old_storage = object->properties();
1645 int new_unused = transition->unused_property_fields();
1646 int new_size = old_storage->length() + new_unused + 1;
1647 Object* result = old_storage->CopySize(new_size);
1648 if (result->IsFailure()) return result;
1649 FixedArray* new_storage = FixedArray::cast(result);
1650 new_storage->set(old_storage->length(), value);
1651
1652 // Set the new property value and do the map transition.
1653 object->set_properties(new_storage);
1654 object->set_map(transition);
1655
1656 // Return the stored value.
1657 return value;
1658}
1659
1660
Ben Murdoch7f4d5bd2010-06-15 11:15:29 +01001661// Used from ic-<arch>.cc.
Steve Blocka7e24c12009-10-30 11:49:00 +00001662Object* KeyedStoreIC_Miss(Arguments args) {
1663 NoHandleAllocation na;
1664 ASSERT(args.length() == 3);
1665 KeyedStoreIC ic;
Steve Block6ded16b2010-05-10 14:33:55 +01001666 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +00001667 return ic.Store(state, args.at<Object>(0), args.at<Object>(1),
1668 args.at<Object>(2));
1669}
1670
1671
Steve Block6ded16b2010-05-10 14:33:55 +01001672void BinaryOpIC::patch(Code* code) {
1673 set_target(code);
1674}
1675
1676
1677const char* BinaryOpIC::GetName(TypeInfo type_info) {
1678 switch (type_info) {
1679 case DEFAULT: return "Default";
1680 case GENERIC: return "Generic";
1681 case HEAP_NUMBERS: return "HeapNumbers";
1682 case STRINGS: return "Strings";
1683 default: return "Invalid";
1684 }
1685}
1686
1687
1688BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
1689 switch (type_info) {
1690 // DEFAULT is mapped to UNINITIALIZED so that calls to DEFAULT stubs
1691 // are not cleared at GC.
1692 case DEFAULT: return UNINITIALIZED;
1693
1694 // Could have mapped GENERIC to MONOMORPHIC just as well but MEGAMORPHIC is
1695 // conceptually closer.
1696 case GENERIC: return MEGAMORPHIC;
1697
1698 default: return MONOMORPHIC;
1699 }
1700}
1701
1702
1703BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Object* left,
1704 Object* right) {
1705 if (left->IsSmi() && right->IsSmi()) {
1706 return GENERIC;
1707 }
1708
1709 if (left->IsNumber() && right->IsNumber()) {
1710 return HEAP_NUMBERS;
1711 }
1712
1713 if (left->IsString() || right->IsString()) {
1714 // Patching for fast string ADD makes sense even if only one of the
1715 // arguments is a string.
1716 return STRINGS;
1717 }
1718
1719 return GENERIC;
1720}
1721
1722
1723// defined in codegen-<arch>.cc
1724Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info);
1725
1726
1727Object* BinaryOp_Patch(Arguments args) {
Leon Clarkeac952652010-07-15 11:15:24 +01001728 ASSERT(args.length() == 5);
Steve Block6ded16b2010-05-10 14:33:55 +01001729
1730 Handle<Object> left = args.at<Object>(0);
1731 Handle<Object> right = args.at<Object>(1);
Leon Clarkeac952652010-07-15 11:15:24 +01001732 int key = Smi::cast(args[2])->value();
1733 Token::Value op = static_cast<Token::Value>(Smi::cast(args[3])->value());
Steve Block6ded16b2010-05-10 14:33:55 +01001734#ifdef DEBUG
Steve Block6ded16b2010-05-10 14:33:55 +01001735 BinaryOpIC::TypeInfo prev_type_info =
Leon Clarkeac952652010-07-15 11:15:24 +01001736 static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
Steve Block6ded16b2010-05-10 14:33:55 +01001737#endif // DEBUG
1738 { HandleScope scope;
1739 BinaryOpIC::TypeInfo type_info = BinaryOpIC::GetTypeInfo(*left, *right);
1740 Handle<Code> code = GetBinaryOpStub(key, type_info);
1741 if (!code.is_null()) {
1742 BinaryOpIC ic;
1743 ic.patch(*code);
1744#ifdef DEBUG
1745 if (FLAG_trace_ic) {
1746 PrintF("[BinaryOpIC (%s->%s)#%s]\n",
1747 BinaryOpIC::GetName(prev_type_info),
1748 BinaryOpIC::GetName(type_info),
1749 Token::Name(op));
1750 }
1751#endif // DEBUG
1752 }
1753 }
1754
Leon Clarkeac952652010-07-15 11:15:24 +01001755 HandleScope scope;
1756 Handle<JSBuiltinsObject> builtins = Top::builtins();
1757
1758 Object* builtin = NULL; // Initialization calms down the compiler.
1759
1760 switch (op) {
1761 case Token::ADD:
1762 builtin = builtins->javascript_builtin(Builtins::ADD);
1763 break;
1764 case Token::SUB:
1765 builtin = builtins->javascript_builtin(Builtins::SUB);
1766 break;
1767 case Token::MUL:
1768 builtin = builtins->javascript_builtin(Builtins::MUL);
1769 break;
1770 case Token::DIV:
1771 builtin = builtins->javascript_builtin(Builtins::DIV);
1772 break;
1773 case Token::MOD:
1774 builtin = builtins->javascript_builtin(Builtins::MOD);
1775 break;
1776 case Token::BIT_AND:
1777 builtin = builtins->javascript_builtin(Builtins::BIT_AND);
1778 break;
1779 case Token::BIT_OR:
1780 builtin = builtins->javascript_builtin(Builtins::BIT_OR);
1781 break;
1782 case Token::BIT_XOR:
1783 builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
1784 break;
1785 case Token::SHR:
1786 builtin = builtins->javascript_builtin(Builtins::SHR);
1787 break;
1788 case Token::SAR:
1789 builtin = builtins->javascript_builtin(Builtins::SAR);
1790 break;
1791 case Token::SHL:
1792 builtin = builtins->javascript_builtin(Builtins::SHL);
1793 break;
1794 default:
1795 UNREACHABLE();
1796 }
1797
1798 Handle<JSFunction> builtin_function(JSFunction::cast(builtin));
1799
1800 bool caught_exception;
1801 Object** builtin_args[] = { right.location() };
1802 Handle<Object> result = Execution::Call(builtin_function,
1803 left,
1804 ARRAY_SIZE(builtin_args),
1805 builtin_args,
1806 &caught_exception);
1807 if (caught_exception) {
1808 return Failure::Exception();
1809 }
Steve Block6ded16b2010-05-10 14:33:55 +01001810 return *result;
1811}
1812
1813
Steve Blocka7e24c12009-10-30 11:49:00 +00001814static Address IC_utilities[] = {
1815#define ADDR(name) FUNCTION_ADDR(name),
1816 IC_UTIL_LIST(ADDR)
1817 NULL
1818#undef ADDR
1819};
1820
1821
1822Address IC::AddressFromUtilityId(IC::UtilityId id) {
1823 return IC_utilities[id];
1824}
1825
1826
1827} } // namespace v8::internal