blob: 58acebcc5d76ffb5e230eb2c99a58433df4894d2 [file] [log] [blame]
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001// Copyright 2006-2009 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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
kasperl@chromium.org71affb52009-05-26 05:44:31 +000038namespace v8 {
39namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000040
41#ifdef DEBUG
kasperl@chromium.orgba86cd62009-05-27 08:46:40 +000042static char TransitionMarkFromState(IC::State state) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043 switch (state) {
44 case UNINITIALIZED: return '0';
ager@chromium.org5ec48922009-05-05 07:25:34 +000045 case PREMONOMORPHIC: return 'P';
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000046 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,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000061 Handle<Object> name,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000062 State old_state,
kasperl@chromium.org71affb52009-05-26 05:44:31 +000063 Code* new_target,
64 const char* extra_info) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000065 if (FLAG_trace_ic) {
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000066 State new_state = StateFrom(new_target,
67 Heap::undefined_value(),
68 Heap::undefined_value());
kasperl@chromium.org71affb52009-05-26 05:44:31 +000069 PrintF("[%s (%c->%c)%s", type,
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000070 TransitionMarkFromState(old_state),
kasperl@chromium.org71affb52009-05-26 05:44:31 +000071 TransitionMarkFromState(new_state),
72 extra_info);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000073 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
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000107#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000108Address 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.
ager@chromium.org4af710e2009-09-15 12:20:11 +0000127 Address addr = pc() - Assembler::kCallTargetAddressOffset;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000128 // Return the address in the original code. This is the place where
ager@chromium.org32912102009-01-16 10:38:43 +0000129 // the call which has been overwritten by the DebugBreakXXX resides
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000130 // and the place where the inline cache system should look.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000131 intptr_t delta =
132 original_code->instruction_start() - code->instruction_start();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000133 return addr + delta;
134}
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000135#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000136
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000137
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
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000156IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
kasper.lund7276f142008-07-30 08:49:36 +0000157 IC::State state = target->ic_state();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000158
159 if (state != MONOMORPHIC) return state;
160 if (receiver->IsUndefined() || receiver->IsNull()) return state;
161
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000162 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.
ricow@chromium.org65fae842010-08-25 15:26:24 +0000168 // IC::GetCodeCacheHolder is not applicable.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000169 return MONOMORPHIC;
170 } else if (cache_holder == PROTOTYPE_MAP &&
171 receiver->GetPrototype()->IsNull()) {
ricow@chromium.org65fae842010-08-25 15:26:24 +0000172 // IC::GetCodeCacheHolder is not applicable.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000173 return MONOMORPHIC;
174 }
ricow@chromium.org65fae842010-08-25 15:26:24 +0000175 Map* map = IC::GetCodeCacheHolder(receiver, cache_holder)->map();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +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.
sgjesse@chromium.org99a37fa2010-03-11 09:23:46 +0000185 int index = map->IndexInCodeCache(name, target);
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000186 if (index >= 0) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000187 // For keyed load/store/call, the most likely cause of cache failure is
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +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();
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000191 if (kind == Code::KEYED_LOAD_IC ||
192 kind == Code::KEYED_STORE_IC ||
193 kind == Code::KEYED_CALL_IC) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000194 return MONOMORPHIC;
195 }
196
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000197 // Remove the target from the code cache to avoid hitting the same
198 // invalid stub again.
ager@chromium.orgce5e87b2010-03-10 10:24:18 +0000199 map->RemoveFromCodeCache(String::cast(name), target, index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000200
201 return MONOMORPHIC_PROTOTYPE_FAILURE;
202 }
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000203
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
ager@chromium.org236ad962008-09-25 09:45:57 +0000208 // JavaScript builtins, we return uninitialized as the state to
209 // force the inline cache back to monomorphic state.
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000210 if (receiver->IsJSBuiltinsObject()) {
kasperl@chromium.orgb9123622008-09-17 14:05:56 +0000211 return UNINITIALIZED;
212 }
213
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000214 return MONOMORPHIC;
215}
216
217
ager@chromium.org236ad962008-09-25 09:45:57 +0000218RelocInfo::Mode IC::ComputeMode() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000219 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();
ager@chromium.org236ad962008-09-25 09:45:57 +0000227 return RelocInfo::NONE;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000228}
229
230
231Failure* IC::TypeError(const char* type,
232 Handle<Object> object,
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000233 Handle<Object> key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000234 HandleScope scope;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000235 Handle<Object> args[2] = { key, object };
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +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.
kasper.lund7276f142008-07-30 08:49:36 +0000253 if (target->ic_state() == DEBUG_BREAK) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000254
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);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000261 case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
ager@chromium.org357bf652010-04-12 11:30:10 +0000262 case Code::BINARY_OP_IC: return; // Clearing these is tricky and does not
263 // make any performance difference.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000264 default: UNREACHABLE();
265 }
266}
267
268
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000269void CallICBase::Clear(Address address, Code* target) {
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000270 State state = target->ic_state();
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000271 if (state == UNINITIALIZED) return;
272 Code* code =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000273 StubCache::FindCallInitialize(target->arguments_count(),
274 target->ic_in_loop(),
275 target->kind());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000276 SetTargetAtAddress(address, code);
277}
278
279
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000280void 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
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000287void KeyedLoadIC::Clear(Address address, Code* target) {
kasper.lund7276f142008-07-30 08:49:36 +0000288 if (target->ic_state() == UNINITIALIZED) return;
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000289 // 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.
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000292 ClearInlinedVersion(address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000293 SetTargetAtAddress(address, initialize_stub());
294}
295
296
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000297void 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);
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +0000302 PatchInlinedContextualLoad(address,
303 Heap::null_value(),
304 Heap::null_value(),
305 true);
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000306}
307
308
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000309void LoadIC::Clear(Address address, Code* target) {
kasper.lund7276f142008-07-30 08:49:36 +0000310 if (target->ic_state() == UNINITIALIZED) return;
ager@chromium.org5ec48922009-05-05 07:25:34 +0000311 ClearInlinedVersion(address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000312 SetTargetAtAddress(address, initialize_stub());
313}
314
315
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000316void StoreIC::ClearInlinedVersion(Address address) {
317 // Reset the map check of the inlined inobject property store (if
318 // present) to guarantee failure by holding an invalid map (the null
319 // value). The offset can be patched to anything.
320 PatchInlinedStore(address, Heap::null_value(), 0);
321}
322
323
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000324void StoreIC::Clear(Address address, Code* target) {
kasper.lund7276f142008-07-30 08:49:36 +0000325 if (target->ic_state() == UNINITIALIZED) return;
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000326 ClearInlinedVersion(address);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000327 SetTargetAtAddress(address, initialize_stub());
328}
329
330
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000331void KeyedStoreIC::ClearInlinedVersion(Address address) {
332 // Insert null as the elements map to check for. This will make
333 // sure that the elements fast-case map check fails so that control
334 // flows to the IC instead of the inlined version.
335 PatchInlinedStore(address, Heap::null_value());
336}
337
338
339void KeyedStoreIC::RestoreInlinedVersion(Address address) {
340 // Restore the fast-case elements map check so that the inlined
341 // version can be used again.
342 PatchInlinedStore(address, Heap::fixed_array_map());
343}
344
345
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000346void KeyedStoreIC::Clear(Address address, Code* target) {
kasper.lund7276f142008-07-30 08:49:36 +0000347 if (target->ic_state() == UNINITIALIZED) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000348 SetTargetAtAddress(address, initialize_stub());
349}
350
351
ager@chromium.org3811b432009-10-28 14:53:37 +0000352Code* KeyedLoadIC::external_array_stub(JSObject::ElementsKind elements_kind) {
353 switch (elements_kind) {
354 case JSObject::EXTERNAL_BYTE_ELEMENTS:
355 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalByteArray);
356 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
357 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedByteArray);
358 case JSObject::EXTERNAL_SHORT_ELEMENTS:
359 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalShortArray);
360 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
361 return Builtins::builtin(
362 Builtins::KeyedLoadIC_ExternalUnsignedShortArray);
363 case JSObject::EXTERNAL_INT_ELEMENTS:
364 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalIntArray);
365 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
366 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalUnsignedIntArray);
367 case JSObject::EXTERNAL_FLOAT_ELEMENTS:
368 return Builtins::builtin(Builtins::KeyedLoadIC_ExternalFloatArray);
369 default:
370 UNREACHABLE();
371 return NULL;
372 }
373}
374
375
376Code* KeyedStoreIC::external_array_stub(JSObject::ElementsKind elements_kind) {
377 switch (elements_kind) {
378 case JSObject::EXTERNAL_BYTE_ELEMENTS:
379 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalByteArray);
380 case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
381 return Builtins::builtin(
382 Builtins::KeyedStoreIC_ExternalUnsignedByteArray);
383 case JSObject::EXTERNAL_SHORT_ELEMENTS:
384 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalShortArray);
385 case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
386 return Builtins::builtin(
387 Builtins::KeyedStoreIC_ExternalUnsignedShortArray);
388 case JSObject::EXTERNAL_INT_ELEMENTS:
389 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalIntArray);
390 case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS:
391 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalUnsignedIntArray);
392 case JSObject::EXTERNAL_FLOAT_ELEMENTS:
393 return Builtins::builtin(Builtins::KeyedStoreIC_ExternalFloatArray);
394 default:
395 UNREACHABLE();
396 return NULL;
397 }
398}
399
400
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000401static bool HasInterceptorGetter(JSObject* object) {
402 return !object->GetNamedInterceptor()->getter()->IsUndefined();
403}
404
405
406static void LookupForRead(Object* object,
407 String* name,
408 LookupResult* lookup) {
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000409 AssertNoAllocation no_gc; // pointers must stay valid
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000410
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000411 // Skip all the objects with named interceptors, but
412 // without actual getter.
413 while (true) {
414 object->Lookup(name, lookup);
415 // Besides normal conditions (property not found or it's not
ager@chromium.org5c838252010-02-19 08:53:10 +0000416 // an interceptor), bail out if lookup is not cacheable: we won't
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000417 // be able to IC it anyway and regular lookup should work fine.
ager@chromium.org5c838252010-02-19 08:53:10 +0000418 if (!lookup->IsFound()
419 || (lookup->type() != INTERCEPTOR)
420 || !lookup->IsCacheable()) {
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000421 return;
422 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000423
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000424 JSObject* holder = lookup->holder();
425 if (HasInterceptorGetter(holder)) {
426 return;
427 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000428
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000429 holder->LocalLookupRealNamedProperty(name, lookup);
ager@chromium.org5c838252010-02-19 08:53:10 +0000430 if (lookup->IsProperty()) {
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000431 ASSERT(lookup->type() != INTERCEPTOR);
432 return;
433 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000434
kasperl@chromium.orge959c182009-07-27 08:59:04 +0000435 Object* proto = holder->GetPrototype();
436 if (proto->IsNull()) {
437 lookup->NotFound();
438 return;
439 }
440
441 object = proto;
442 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000443}
444
445
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000446Object* CallICBase::TryCallAsFunction(Object* object) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000447 HandleScope scope;
448 Handle<Object> target(object);
449 Handle<Object> delegate = Execution::GetFunctionDelegate(target);
450
451 if (delegate->IsJSFunction()) {
452 // Patch the receiver and use the delegate as the function to
453 // invoke. This is used for invoking objects as if they were
454 // functions.
455 const int argc = this->target()->arguments_count();
456 StackFrameLocator locator;
457 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
458 int index = frame->ComputeExpressionsCount() - (argc + 1);
459 frame->SetExpression(index, *target);
460 }
461
462 return *delegate;
463}
464
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000465
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000466void CallICBase::ReceiverToObject(Handle<Object> object) {
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000467 HandleScope scope;
468 Handle<Object> receiver(object);
469
470 // Change the receiver to the result of calling ToObject on it.
471 const int argc = this->target()->arguments_count();
472 StackFrameLocator locator;
473 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
474 int index = frame->ComputeExpressionsCount() - (argc + 1);
475 frame->SetExpression(index, *Factory::ToObject(object));
476}
477
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000478
lrn@chromium.org303ada72010-10-27 09:33:13 +0000479MaybeObject* CallICBase::LoadFunction(State state,
480 Handle<Object> object,
481 Handle<String> name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000482 // If the object is undefined or null it's illegal to try to get any
483 // of its properties; throw a TypeError in that case.
484 if (object->IsUndefined() || object->IsNull()) {
485 return TypeError("non_object_property_call", object, name);
486 }
487
sgjesse@chromium.orgb302e562010-02-03 11:26:59 +0000488 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
489 ReceiverToObject(object);
490 }
491
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000492 // Check if the name is trivially convertible to an index and get
493 // the element if so.
494 uint32_t index;
495 if (name->AsArrayIndex(&index)) {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000496 Object* result;
497 { MaybeObject* maybe_result = object->GetElement(index);
498 if (!maybe_result->ToObject(&result)) return maybe_result;
499 }
500
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000501 if (result->IsJSFunction()) return result;
502
503 // Try to find a suitable function delegate for the object at hand.
504 result = TryCallAsFunction(result);
505 if (result->IsJSFunction()) return result;
506
507 // Otherwise, it will fail in the lookup step.
508 }
509
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000510 // Lookup the property in the object.
511 LookupResult lookup;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000512 LookupForRead(*object, *name, &lookup);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000513
ager@chromium.org5c838252010-02-19 08:53:10 +0000514 if (!lookup.IsProperty()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000515 // If the object does not have the requested property, check which
516 // exception we need to throw.
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000517 if (IsContextual(object)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000518 return ReferenceError("not_defined", name);
519 }
520 return TypeError("undefined_method", object, name);
521 }
522
523 // Lookup is valid: Update inline cache and stub cache.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000524 if (FLAG_use_ic) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000525 UpdateCaches(&lookup, state, object, name);
526 }
527
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000528 // Get the property.
529 PropertyAttributes attr;
lrn@chromium.org303ada72010-10-27 09:33:13 +0000530 Object* result;
531 { MaybeObject* maybe_result =
532 object->GetProperty(*object, &lookup, *name, &attr);
533 if (!maybe_result->ToObject(&result)) return maybe_result;
534 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000535 if (lookup.type() == INTERCEPTOR) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000536 // If the object does not have the requested property, check which
537 // exception we need to throw.
538 if (attr == ABSENT) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000539 if (IsContextual(object)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000540 return ReferenceError("not_defined", name);
541 }
542 return TypeError("undefined_method", object, name);
543 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000544 }
545
546 ASSERT(result != Heap::the_hole_value());
547
548 if (result->IsJSFunction()) {
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000549#ifdef ENABLE_DEBUGGER_SUPPORT
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000550 // Handle stepping into a function if step into is active.
551 if (Debug::StepInActive()) {
552 // Protect the result in a handle as the debugger can allocate and might
553 // cause GC.
554 HandleScope scope;
555 Handle<JSFunction> function(JSFunction::cast(result));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000556 Debug::HandleStepIn(function, object, fp(), false);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000557 return *function;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558 }
ager@chromium.org65dad4b2009-04-23 08:48:43 +0000559#endif
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +0000560
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000561 return result;
562 }
563
564 // Try to find a suitable function delegate for the object at hand.
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000565 result = TryCallAsFunction(result);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000566 MaybeObject* answer = result;
567 if (!result->IsJSFunction()) {
568 answer = TypeError("property_not_function", object, name);
569 }
570 return answer;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000571}
572
573
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000574void CallICBase::UpdateCaches(LookupResult* lookup,
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000575 State state,
576 Handle<Object> object,
577 Handle<String> name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000578 // Bail out if we didn't find a result.
ager@chromium.org5c838252010-02-19 08:53:10 +0000579 if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000580
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000581 if (lookup->holder() != *object &&
582 HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) {
583 // Suppress optimization for prototype chains with slow properties objects
584 // in the middle.
585 return;
586 }
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000587
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000588 // Compute the number of arguments.
589 int argc = target()->arguments_count();
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000590 InLoopFlag in_loop = target()->ic_in_loop();
lrn@chromium.org303ada72010-10-27 09:33:13 +0000591 MaybeObject* maybe_code = NULL;
592 Object* code;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000593 if (state == UNINITIALIZED) {
594 // This is the first time we execute this inline cache.
595 // Set the target to the pre monomorphic stub to delay
596 // setting the monomorphic state.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000597 maybe_code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000598 } else if (state == MONOMORPHIC) {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000599 maybe_code = StubCache::ComputeCallMegamorphic(argc, in_loop, kind_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000600 } else {
601 // Compute monomorphic stub.
602 switch (lookup->type()) {
603 case FIELD: {
604 int index = lookup->GetFieldIndex();
lrn@chromium.org303ada72010-10-27 09:33:13 +0000605 maybe_code = StubCache::ComputeCallField(argc,
606 in_loop,
607 kind_,
608 *name,
609 *object,
610 lookup->holder(),
611 index);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000612 break;
613 }
614 case CONSTANT_FUNCTION: {
615 // Get the constant function and compute the code stub for this
616 // call; used for rewriting to monomorphic state and making sure
617 // that the code stub is in the stub cache.
618 JSFunction* function = lookup->GetConstantFunction();
lrn@chromium.org303ada72010-10-27 09:33:13 +0000619 maybe_code = StubCache::ComputeCallConstant(argc,
620 in_loop,
621 kind_,
622 *name,
623 *object,
624 lookup->holder(),
625 function);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000626 break;
627 }
628 case NORMAL: {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000629 if (!object->IsJSObject()) return;
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000630 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
631
632 if (lookup->holder()->IsGlobalObject()) {
633 GlobalObject* global = GlobalObject::cast(lookup->holder());
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000634 JSGlobalPropertyCell* cell =
635 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
kasperl@chromium.org22cffcd2009-07-02 11:00:57 +0000636 if (!cell->value()->IsJSFunction()) return;
637 JSFunction* function = JSFunction::cast(cell->value());
lrn@chromium.org303ada72010-10-27 09:33:13 +0000638 maybe_code = StubCache::ComputeCallGlobal(argc,
639 in_loop,
640 kind_,
641 *name,
642 *receiver,
643 global,
644 cell,
645 function);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000646 } else {
647 // There is only one shared stub for calling normalized
648 // properties. It does not traverse the prototype chain, so the
649 // property must be found in the receiver for the stub to be
650 // applicable.
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000651 if (lookup->holder() != *receiver) return;
lrn@chromium.org303ada72010-10-27 09:33:13 +0000652 maybe_code = StubCache::ComputeCallNormal(argc,
653 in_loop,
654 kind_,
655 *name,
656 *receiver);
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000657 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000658 break;
659 }
660 case INTERCEPTOR: {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000661 ASSERT(HasInterceptorGetter(lookup->holder()));
lrn@chromium.org303ada72010-10-27 09:33:13 +0000662 maybe_code = StubCache::ComputeCallInterceptor(argc,
663 kind_,
664 *name,
665 *object,
666 lookup->holder());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000667 break;
668 }
669 default:
670 return;
671 }
672 }
673
674 // If we're unable to compute the stub (not enough memory left), we
675 // simply avoid updating the caches.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000676 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000677
678 // Patch the call site depending on the state of the cache.
kasperl@chromium.org71affb52009-05-26 05:44:31 +0000679 if (state == UNINITIALIZED ||
680 state == PREMONOMORPHIC ||
681 state == MONOMORPHIC ||
ager@chromium.org3bf7b912008-11-17 09:09:45 +0000682 state == MONOMORPHIC_PROTOTYPE_FAILURE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000683 set_target(Code::cast(code));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000684 } else if (state == MEGAMORPHIC) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000685 // Cache code holding map should be consistent with
686 // GenerateMonomorphicCacheProbe. It is not the map which holds the stub.
687 Map* map = JSObject::cast(object->IsJSObject() ? *object :
688 object->GetPrototype())->map();
689
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000690 // Update the stub cache.
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000691 StubCache::Set(*name, map, Code::cast(code));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000692 }
693
694#ifdef DEBUG
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000695 TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
696 name, state, target(), in_loop ? " (in-loop)" : "");
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697#endif
698}
699
700
lrn@chromium.org303ada72010-10-27 09:33:13 +0000701MaybeObject* KeyedCallIC::LoadFunction(State state,
702 Handle<Object> object,
703 Handle<Object> key) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000704 if (key->IsSymbol()) {
705 return CallICBase::LoadFunction(state, object, Handle<String>::cast(key));
706 }
707
708 if (object->IsUndefined() || object->IsNull()) {
709 return TypeError("non_object_property_call", object, key);
710 }
711
712 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
713 ReceiverToObject(object);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000714 }
715
716 if (FLAG_use_ic && state != MEGAMORPHIC && !object->IsAccessCheckNeeded()) {
717 int argc = target()->arguments_count();
718 InLoopFlag in_loop = target()->ic_in_loop();
lrn@chromium.org303ada72010-10-27 09:33:13 +0000719 MaybeObject* maybe_code = StubCache::ComputeCallMegamorphic(
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000720 argc, in_loop, Code::KEYED_CALL_IC);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000721 Object* code;
722 if (maybe_code->ToObject(&code)) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000723 set_target(Code::cast(code));
724#ifdef DEBUG
725 TraceIC(
726 "KeyedCallIC", key, state, target(), in_loop ? " (in-loop)" : "");
727#endif
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000728 }
729 }
lrn@chromium.org303ada72010-10-27 09:33:13 +0000730 Object* result;
731 { MaybeObject* maybe_result = Runtime::GetObjectProperty(object, key);
732 if (!maybe_result->ToObject(&result)) return maybe_result;
733 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000734 if (result->IsJSFunction()) return result;
735 result = TryCallAsFunction(result);
lrn@chromium.org303ada72010-10-27 09:33:13 +0000736 MaybeObject* answer = result;
737 if (!result->IsJSFunction()) {
738 answer = TypeError("property_not_function", object, key);
739 }
740 return answer;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +0000741}
742
743
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000744#ifdef DEBUG
745#define TRACE_IC_NAMED(msg, name) \
746 if (FLAG_trace_ic) PrintF(msg, *(name)->ToCString())
747#else
748#define TRACE_IC_NAMED(msg, name)
749#endif
750
751
lrn@chromium.org303ada72010-10-27 09:33:13 +0000752MaybeObject* LoadIC::Load(State state,
753 Handle<Object> object,
754 Handle<String> name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000755 // If the object is undefined or null it's illegal to try to get any
756 // of its properties; throw a TypeError in that case.
757 if (object->IsUndefined() || object->IsNull()) {
758 return TypeError("non_object_property_load", object, name);
759 }
760
761 if (FLAG_use_ic) {
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000762 // Use specialized code for getting the length of strings and
763 // string wrapper objects. The length property of string wrapper
764 // objects is read-only and therefore always returns the length of
765 // the underlying string value. See ECMA-262 15.5.5.1.
766 if ((object->IsString() || object->IsStringWrapper()) &&
767 name->Equals(Heap::length_symbol())) {
768 HandleScope scope;
769 // Get the string if we have a string wrapper object.
770 if (object->IsJSValue()) {
771 object = Handle<Object>(Handle<JSValue>::cast(object)->value());
772 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000773#ifdef DEBUG
774 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n");
775#endif
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000776 Map* map = HeapObject::cast(*object)->map();
777 if (object->IsString()) {
778 const int offset = String::kLengthOffset;
779 PatchInlinedLoad(address(), map, offset);
780 }
781
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000782 Code* target = NULL;
kasperl@chromium.org9fe21c62008-10-28 08:53:51 +0000783 target = Builtins::builtin(Builtins::LoadIC_StringLength);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000784 set_target(target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000785 return Smi::FromInt(String::cast(*object)->length());
786 }
787
788 // Use specialized code for getting the length of arrays.
789 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
790#ifdef DEBUG
791 if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
792#endif
sgjesse@chromium.org720dc0b2010-05-10 09:25:39 +0000793 Map* map = HeapObject::cast(*object)->map();
794 const int offset = JSArray::kLengthOffset;
795 PatchInlinedLoad(address(), map, offset);
796
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000797 Code* target = Builtins::builtin(Builtins::LoadIC_ArrayLength);
798 set_target(target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000799 return JSArray::cast(*object)->length();
800 }
801
802 // Use specialized code for getting prototype of functions.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +0000803 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
804 JSFunction::cast(*object)->should_have_prototype()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000805#ifdef DEBUG
806 if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
807#endif
808 Code* target = Builtins::builtin(Builtins::LoadIC_FunctionPrototype);
809 set_target(target);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000810 return Accessors::FunctionGetPrototype(*object, 0);
811 }
812 }
813
814 // Check if the name is trivially convertible to an index and get
815 // the element if so.
816 uint32_t index;
817 if (name->AsArrayIndex(&index)) return object->GetElement(index);
818
819 // Named lookup in the object.
820 LookupResult lookup;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000821 LookupForRead(*object, *name, &lookup);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000822
ager@chromium.org5c838252010-02-19 08:53:10 +0000823 // If we did not find a property, check if we need to throw an exception.
824 if (!lookup.IsProperty()) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000825 if (FLAG_strict || IsContextual(object)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000826 return ReferenceError("not_defined", name);
827 }
ager@chromium.org8bb60582008-12-11 12:02:20 +0000828 LOG(SuspectReadEvent(*name, *object));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000829 }
830
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000831 bool can_be_inlined_precheck =
ager@chromium.org5ec48922009-05-05 07:25:34 +0000832 FLAG_use_ic &&
ager@chromium.org5c838252010-02-19 08:53:10 +0000833 lookup.IsProperty() &&
ager@chromium.org5ec48922009-05-05 07:25:34 +0000834 lookup.IsCacheable() &&
835 lookup.holder() == *object &&
ager@chromium.org5ec48922009-05-05 07:25:34 +0000836 !object->IsAccessCheckNeeded();
837
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000838 bool can_be_inlined =
839 can_be_inlined_precheck &&
840 state == PREMONOMORPHIC &&
841 lookup.type() == FIELD;
842
843 bool can_be_inlined_contextual =
844 can_be_inlined_precheck &&
845 state == UNINITIALIZED &&
846 lookup.holder()->IsGlobalObject() &&
847 lookup.type() == NORMAL;
848
ager@chromium.org5ec48922009-05-05 07:25:34 +0000849 if (can_be_inlined) {
850 Map* map = lookup.holder()->map();
851 // Property's index in the properties array. If negative we have
852 // an inobject property.
853 int index = lookup.GetFieldIndex() - map->inobject_properties();
854 if (index < 0) {
855 // Index is an offset from the end of the object.
856 int offset = map->instance_size() + (index * kPointerSize);
857 if (PatchInlinedLoad(address(), map, offset)) {
858 set_target(megamorphic_stub());
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000859 TRACE_IC_NAMED("[LoadIC : inline patch %s]\n", name);
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +0000860 return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000861 } else {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000862 TRACE_IC_NAMED("[LoadIC : no inline patch %s (patching failed)]\n",
863 name);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000864 }
865 } else {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000866 TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inobject)]\n", name);
867 }
868 } else if (can_be_inlined_contextual) {
869 Map* map = lookup.holder()->map();
870 JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(
871 lookup.holder()->property_dictionary()->ValueAt(
872 lookup.GetDictionaryEntry()));
ricow@chromium.orgeb7c1442010-10-04 08:54:21 +0000873 if (PatchInlinedContextualLoad(address(),
874 map,
875 cell,
876 lookup.IsDontDelete())) {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000877 set_target(megamorphic_stub());
878 TRACE_IC_NAMED("[LoadIC : inline contextual patch %s]\n", name);
879 ASSERT(cell->value() != Heap::the_hole_value());
880 return cell->value();
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000881 }
882 } else {
883 if (FLAG_use_ic && state == PREMONOMORPHIC) {
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000884 TRACE_IC_NAMED("[LoadIC : no inline patch %s (not inlinable)]\n", name);
ager@chromium.org5ec48922009-05-05 07:25:34 +0000885 }
886 }
887
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000888 // Update inline cache and stub cache.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +0000889 if (FLAG_use_ic) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000890 UpdateCaches(&lookup, state, object, name);
891 }
892
893 PropertyAttributes attr;
ager@chromium.org5c838252010-02-19 08:53:10 +0000894 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000895 // Get the property.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000896 Object* result;
897 { MaybeObject* maybe_result =
898 object->GetProperty(*object, &lookup, *name, &attr);
899 if (!maybe_result->ToObject(&result)) return maybe_result;
900 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000901 // If the property is not present, check if we need to throw an
902 // exception.
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +0000903 if (attr == ABSENT && IsContextual(object)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000904 return ReferenceError("not_defined", name);
905 }
906 return result;
907 }
908
909 // Get the property.
910 return object->GetProperty(*object, &lookup, *name, &attr);
911}
912
913
914void LoadIC::UpdateCaches(LookupResult* lookup,
915 State state,
916 Handle<Object> object,
917 Handle<String> name) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000918 // Bail out if the result is not cacheable.
919 if (!lookup->IsCacheable()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000920
921 // Loading properties from values is not common, so don't try to
922 // deal with non-JS objects here.
923 if (!object->IsJSObject()) return;
924 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
925
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +0000926 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
927
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000928 // Compute the code stub for this load.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000929 MaybeObject* maybe_code = NULL;
930 Object* code;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000931 if (state == UNINITIALIZED) {
932 // This is the first time we execute this inline cache.
933 // Set the target to the pre monomorphic stub to delay
934 // setting the monomorphic state.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000935 maybe_code = pre_monomorphic_stub();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000936 } else if (!lookup->IsProperty()) {
937 // Nonexistent property. The result is undefined.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000938 maybe_code = StubCache::ComputeLoadNonexistent(*name, *receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000939 } else {
940 // Compute monomorphic stub.
941 switch (lookup->type()) {
942 case FIELD: {
lrn@chromium.org303ada72010-10-27 09:33:13 +0000943 maybe_code = StubCache::ComputeLoadField(*name, *receiver,
944 lookup->holder(),
945 lookup->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000946 break;
947 }
948 case CONSTANT_FUNCTION: {
949 Object* constant = lookup->GetConstantFunction();
lrn@chromium.org303ada72010-10-27 09:33:13 +0000950 maybe_code = StubCache::ComputeLoadConstant(*name, *receiver,
951 lookup->holder(), constant);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000952 break;
953 }
954 case NORMAL: {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000955 if (lookup->holder()->IsGlobalObject()) {
956 GlobalObject* global = GlobalObject::cast(lookup->holder());
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000957 JSGlobalPropertyCell* cell =
958 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
lrn@chromium.org303ada72010-10-27 09:33:13 +0000959 maybe_code = StubCache::ComputeLoadGlobal(*name,
960 *receiver,
961 global,
962 cell,
963 lookup->IsDontDelete());
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000964 } else {
965 // There is only one shared stub for loading normalized
966 // properties. It does not traverse the prototype chain, so the
967 // property must be found in the receiver for the stub to be
968 // applicable.
969 if (lookup->holder() != *receiver) return;
lrn@chromium.org303ada72010-10-27 09:33:13 +0000970 maybe_code = StubCache::ComputeLoadNormal();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000971 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000972 break;
973 }
974 case CALLBACKS: {
975 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
976 AccessorInfo* callback =
977 AccessorInfo::cast(lookup->GetCallbackObject());
978 if (v8::ToCData<Address>(callback->getter()) == 0) return;
lrn@chromium.org303ada72010-10-27 09:33:13 +0000979 maybe_code = StubCache::ComputeLoadCallback(*name, *receiver,
980 lookup->holder(), callback);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000981 break;
982 }
983 case INTERCEPTOR: {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +0000984 ASSERT(HasInterceptorGetter(lookup->holder()));
lrn@chromium.org303ada72010-10-27 09:33:13 +0000985 maybe_code = StubCache::ComputeLoadInterceptor(*name, *receiver,
986 lookup->holder());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000987 break;
988 }
989 default:
990 return;
991 }
992 }
993
994 // If we're unable to compute the stub (not enough memory left), we
995 // simply avoid updating the caches.
lrn@chromium.org303ada72010-10-27 09:33:13 +0000996 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000997
998 // Patch the call site depending on the state of the cache.
999 if (state == UNINITIALIZED || state == PREMONOMORPHIC ||
1000 state == MONOMORPHIC_PROTOTYPE_FAILURE) {
1001 set_target(Code::cast(code));
1002 } else if (state == MONOMORPHIC) {
1003 set_target(megamorphic_stub());
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001004 } else if (state == MEGAMORPHIC) {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001005 // Cache code holding map should be consistent with
1006 // GenerateMonomorphicCacheProbe.
1007 Map* map = JSObject::cast(object->IsJSObject() ? *object :
1008 object->GetPrototype())->map();
1009
1010 StubCache::Set(*name, map, Code::cast(code));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001011 }
1012
1013#ifdef DEBUG
1014 TraceIC("LoadIC", name, state, target());
1015#endif
1016}
1017
1018
lrn@chromium.org303ada72010-10-27 09:33:13 +00001019MaybeObject* KeyedLoadIC::Load(State state,
1020 Handle<Object> object,
1021 Handle<Object> key) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001022 if (key->IsSymbol()) {
1023 Handle<String> name = Handle<String>::cast(key);
1024
1025 // If the object is undefined or null it's illegal to try to get any
1026 // of its properties; throw a TypeError in that case.
1027 if (object->IsUndefined() || object->IsNull()) {
1028 return TypeError("non_object_property_load", object, name);
1029 }
1030
kasperl@chromium.orgaa95aeb2009-07-28 10:59:50 +00001031 if (FLAG_use_ic) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001032 // Use specialized code for getting the length of strings.
1033 if (object->IsString() && name->Equals(Heap::length_symbol())) {
1034 Handle<String> string = Handle<String>::cast(object);
1035 Object* code = NULL;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001036 { MaybeObject* maybe_code =
1037 StubCache::ComputeKeyedLoadStringLength(*name, *string);
1038 if (!maybe_code->ToObject(&code)) return maybe_code;
1039 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001040 set_target(Code::cast(code));
1041#ifdef DEBUG
1042 TraceIC("KeyedLoadIC", name, state, target());
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001043#endif // DEBUG
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001044 return Smi::FromInt(string->length());
1045 }
1046
1047 // Use specialized code for getting the length of arrays.
1048 if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
1049 Handle<JSArray> array = Handle<JSArray>::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001050 Object* code;
1051 { MaybeObject* maybe_code =
1052 StubCache::ComputeKeyedLoadArrayLength(*name, *array);
1053 if (!maybe_code->ToObject(&code)) return maybe_code;
1054 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001055 set_target(Code::cast(code));
1056#ifdef DEBUG
1057 TraceIC("KeyedLoadIC", name, state, target());
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001058#endif // DEBUG
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001059 return JSArray::cast(*object)->length();
1060 }
1061
1062 // Use specialized code for getting prototype of functions.
kmillikin@chromium.org4111b802010-05-03 10:34:42 +00001063 if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol()) &&
1064 JSFunction::cast(*object)->should_have_prototype()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001065 Handle<JSFunction> function = Handle<JSFunction>::cast(object);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001066 Object* code;
1067 { MaybeObject* maybe_code =
1068 StubCache::ComputeKeyedLoadFunctionPrototype(*name, *function);
1069 if (!maybe_code->ToObject(&code)) return maybe_code;
1070 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001071 set_target(Code::cast(code));
1072#ifdef DEBUG
1073 TraceIC("KeyedLoadIC", name, state, target());
kasperl@chromium.orge959c182009-07-27 08:59:04 +00001074#endif // DEBUG
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001075 return Accessors::FunctionGetPrototype(*object, 0);
1076 }
1077 }
1078
1079 // Check if the name is trivially convertible to an index and get
1080 // the element or char if so.
1081 uint32_t index = 0;
1082 if (name->AsArrayIndex(&index)) {
1083 HandleScope scope;
1084 // Rewrite to the generic keyed load stub.
1085 if (FLAG_use_ic) set_target(generic_stub());
1086 return Runtime::GetElementOrCharAt(object, index);
1087 }
1088
1089 // Named lookup.
1090 LookupResult lookup;
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001091 LookupForRead(*object, *name, &lookup);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001092
ager@chromium.org5c838252010-02-19 08:53:10 +00001093 // If we did not find a property, check if we need to throw an exception.
1094 if (!lookup.IsProperty()) {
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00001095 if (FLAG_strict || IsContextual(object)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001096 return ReferenceError("not_defined", name);
1097 }
1098 }
1099
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001100 if (FLAG_use_ic) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001101 UpdateCaches(&lookup, state, object, name);
1102 }
1103
1104 PropertyAttributes attr;
ager@chromium.org5c838252010-02-19 08:53:10 +00001105 if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001106 // Get the property.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001107 Object* result;
1108 { MaybeObject* maybe_result =
1109 object->GetProperty(*object, &lookup, *name, &attr);
1110 if (!maybe_result->ToObject(&result)) return maybe_result;
1111 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001112 // If the property is not present, check if we need to throw an
1113 // exception.
kmillikin@chromium.org13bd2942009-12-16 15:36:05 +00001114 if (attr == ABSENT && IsContextual(object)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001115 return ReferenceError("not_defined", name);
1116 }
1117 return result;
1118 }
1119
1120 return object->GetProperty(*object, &lookup, *name, &attr);
1121 }
1122
1123 // Do not use ICs for objects that require access checks (including
1124 // the global object).
1125 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
1126
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001127 if (use_ic) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001128 Code* stub = generic_stub();
fschneider@chromium.org0c20e672010-01-14 15:28:53 +00001129 if (object->IsString() && key->IsNumber()) {
1130 stub = string_stub();
1131 } else if (object->IsJSObject()) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001132 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1133 if (receiver->HasExternalArrayElements()) {
1134 stub = external_array_stub(receiver->GetElementsKind());
ager@chromium.org5c838252010-02-19 08:53:10 +00001135 } else if (receiver->HasIndexedInterceptor()) {
1136 stub = indexed_interceptor_stub();
ager@chromium.org3811b432009-10-28 14:53:37 +00001137 }
1138 }
1139 set_target(stub);
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001140 // For JSObjects with fast elements that are not value wrappers
1141 // and that do not have indexed interceptors, we initialize the
1142 // inlined fast case (if present) by patching the inlined map
1143 // check.
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001144 if (object->IsJSObject() &&
1145 !object->IsJSValue() &&
fschneider@chromium.org40b9da32010-06-28 11:29:21 +00001146 !JSObject::cast(*object)->HasIndexedInterceptor() &&
1147 JSObject::cast(*object)->HasFastElements()) {
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001148 Map* map = JSObject::cast(*object)->map();
ager@chromium.org5ec48922009-05-05 07:25:34 +00001149 PatchInlinedLoad(address(), map);
christian.plesner.hansen@gmail.com37abdec2009-01-06 14:43:28 +00001150 }
1151 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001152
1153 // Get the property.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001154 return Runtime::GetObjectProperty(object, key);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001155}
1156
1157
1158void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
1159 Handle<Object> object, Handle<String> name) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001160 // Bail out if we didn't find a result.
ager@chromium.org5c838252010-02-19 08:53:10 +00001161 if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001162
1163 if (!object->IsJSObject()) return;
1164 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1165
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001166 if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
1167
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001168 // Compute the code stub for this load.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001169 MaybeObject* maybe_code = NULL;
1170 Object* code;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001171
1172 if (state == UNINITIALIZED) {
1173 // This is the first time we execute this inline cache.
1174 // Set the target to the pre monomorphic stub to delay
1175 // setting the monomorphic state.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001176 maybe_code = pre_monomorphic_stub();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001177 } else {
1178 // Compute a monomorphic stub.
1179 switch (lookup->type()) {
1180 case FIELD: {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001181 maybe_code = StubCache::ComputeKeyedLoadField(*name, *receiver,
1182 lookup->holder(),
1183 lookup->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001184 break;
1185 }
1186 case CONSTANT_FUNCTION: {
1187 Object* constant = lookup->GetConstantFunction();
lrn@chromium.org303ada72010-10-27 09:33:13 +00001188 maybe_code = StubCache::ComputeKeyedLoadConstant(*name,
1189 *receiver,
1190 lookup->holder(),
1191 constant);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001192 break;
1193 }
1194 case CALLBACKS: {
1195 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
1196 AccessorInfo* callback =
1197 AccessorInfo::cast(lookup->GetCallbackObject());
1198 if (v8::ToCData<Address>(callback->getter()) == 0) return;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001199 maybe_code = StubCache::ComputeKeyedLoadCallback(*name,
1200 *receiver,
1201 lookup->holder(),
1202 callback);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001203 break;
1204 }
1205 case INTERCEPTOR: {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001206 ASSERT(HasInterceptorGetter(lookup->holder()));
lrn@chromium.org303ada72010-10-27 09:33:13 +00001207 maybe_code = StubCache::ComputeKeyedLoadInterceptor(*name, *receiver,
1208 lookup->holder());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001209 break;
1210 }
1211 default: {
1212 // Always rewrite to the generic case so that we do not
1213 // repeatedly try to rewrite.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001214 maybe_code = generic_stub();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001215 break;
1216 }
1217 }
1218 }
1219
1220 // If we're unable to compute the stub (not enough memory left), we
1221 // simply avoid updating the caches.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001222 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001223
1224 // Patch the call site depending on the state of the cache. Make
1225 // sure to always rewrite from monomorphic to megamorphic.
1226 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
1227 if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
1228 set_target(Code::cast(code));
1229 } else if (state == MONOMORPHIC) {
1230 set_target(megamorphic_stub());
1231 }
1232
1233#ifdef DEBUG
1234 TraceIC("KeyedLoadIC", name, state, target());
1235#endif
1236}
1237
1238
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001239static bool StoreICableLookup(LookupResult* lookup) {
1240 // Bail out if we didn't find a result.
ager@chromium.org5c838252010-02-19 08:53:10 +00001241 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return false;
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001242
1243 // If the property is read-only, we leave the IC in its current
1244 // state.
1245 if (lookup->IsReadOnly()) return false;
1246
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001247 return true;
1248}
1249
1250
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001251static bool LookupForWrite(JSObject* object,
1252 String* name,
1253 LookupResult* lookup) {
ager@chromium.org5aa501c2009-06-23 07:57:28 +00001254 object->LocalLookup(name, lookup);
1255 if (!StoreICableLookup(lookup)) {
1256 return false;
1257 }
1258
1259 if (lookup->type() == INTERCEPTOR) {
1260 if (object->GetNamedInterceptor()->setter()->IsUndefined()) {
1261 object->LocalLookupRealNamedProperty(name, lookup);
1262 return StoreICableLookup(lookup);
1263 }
1264 }
1265
1266 return true;
1267}
1268
1269
lrn@chromium.org303ada72010-10-27 09:33:13 +00001270MaybeObject* StoreIC::Store(State state,
1271 Handle<Object> object,
1272 Handle<String> name,
1273 Handle<Object> value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001274 // If the object is undefined or null it's illegal to try to set any
1275 // properties on it; throw a TypeError in that case.
1276 if (object->IsUndefined() || object->IsNull()) {
1277 return TypeError("non_object_property_store", object, name);
1278 }
1279
1280 // Ignore stores where the receiver is not a JSObject.
1281 if (!object->IsJSObject()) return *value;
1282 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1283
1284 // Check if the given name is an array index.
1285 uint32_t index;
1286 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001287 HandleScope scope;
1288 Handle<Object> result = SetElement(receiver, index, value);
1289 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001290 return *value;
1291 }
1292
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001293 // Use specialized code for setting the length of arrays.
1294 if (receiver->IsJSArray()
1295 && name->Equals(Heap::length_symbol())
1296 && receiver->AllowsSetElementsLength()) {
1297#ifdef DEBUG
1298 if (FLAG_trace_ic) PrintF("[StoreIC : +#length /array]\n");
1299#endif
1300 Code* target = Builtins::builtin(Builtins::StoreIC_ArrayLength);
1301 set_target(target);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001302 return receiver->SetProperty(*name, *value, NONE);
1303 }
1304
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001305 // Lookup the property locally in the receiver.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001306 if (FLAG_use_ic && !receiver->IsJSGlobalProxy()) {
1307 LookupResult lookup;
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +00001308
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001309 if (LookupForWrite(*receiver, *name, &lookup)) {
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +00001310 bool can_be_inlined =
1311 state == UNINITIALIZED &&
1312 lookup.IsProperty() &&
1313 lookup.holder() == *receiver &&
1314 lookup.type() == FIELD &&
1315 !receiver->IsAccessCheckNeeded();
1316
1317 if (can_be_inlined) {
1318 Map* map = lookup.holder()->map();
1319 // Property's index in the properties array. If negative we have
1320 // an inobject property.
1321 int index = lookup.GetFieldIndex() - map->inobject_properties();
1322 if (index < 0) {
1323 // Index is an offset from the end of the object.
1324 int offset = map->instance_size() + (index * kPointerSize);
1325 if (PatchInlinedStore(address(), map, offset)) {
1326 set_target(megamorphic_stub());
1327#ifdef DEBUG
1328 if (FLAG_trace_ic) {
1329 PrintF("[StoreIC : inline patch %s]\n", *name->ToCString());
1330 }
1331#endif
1332 return receiver->SetProperty(*name, *value, NONE);
1333#ifdef DEBUG
1334
1335 } else {
1336 if (FLAG_trace_ic) {
1337 PrintF("[StoreIC : no inline patch %s (patching failed)]\n",
1338 *name->ToCString());
1339 }
1340 }
1341 } else {
1342 if (FLAG_trace_ic) {
1343 PrintF("[StoreIC : no inline patch %s (not inobject)]\n",
1344 *name->ToCString());
1345 }
1346 }
1347 } else {
1348 if (state == PREMONOMORPHIC) {
1349 if (FLAG_trace_ic) {
1350 PrintF("[StoreIC : no inline patch %s (not inlinable)]\n",
1351 *name->ToCString());
1352#endif
1353 }
1354 }
1355 }
1356
1357 // If no inlined store ic was patched, generate a stub for this
1358 // store.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001359 UpdateCaches(&lookup, state, receiver, name, value);
1360 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001361 }
1362
1363 // Set the property.
1364 return receiver->SetProperty(*name, *value, NONE);
1365}
1366
1367
1368void StoreIC::UpdateCaches(LookupResult* lookup,
1369 State state,
1370 Handle<JSObject> receiver,
1371 Handle<String> name,
1372 Handle<Object> value) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001373 // Skip JSGlobalProxy.
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001374 ASSERT(!receiver->IsJSGlobalProxy());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001375
ager@chromium.orgeadaf222009-06-16 09:43:10 +00001376 ASSERT(StoreICableLookup(lookup));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001377
1378 // If the property has a non-field type allowing map transitions
1379 // where there is extra room in the object, we leave the IC in its
1380 // current state.
1381 PropertyType type = lookup->type();
1382
1383 // Compute the code stub for this store; used for rewriting to
1384 // monomorphic state and making sure that the code stub is in the
1385 // stub cache.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001386 MaybeObject* maybe_code = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001387 Object* code = NULL;
1388 switch (type) {
1389 case FIELD: {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001390 maybe_code = StubCache::ComputeStoreField(*name, *receiver,
1391 lookup->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001392 break;
1393 }
1394 case MAP_TRANSITION: {
1395 if (lookup->GetAttributes() != NONE) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001396 HandleScope scope;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001397 ASSERT(type == MAP_TRANSITION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001398 Handle<Map> transition(lookup->GetTransitionMap());
1399 int index = transition->PropertyIndexFor(*name);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001400 maybe_code = StubCache::ComputeStoreField(*name, *receiver,
1401 index, *transition);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001402 break;
1403 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001404 case NORMAL: {
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001405 if (receiver->IsGlobalObject()) {
1406 // The stub generated for the global object picks the value directly
1407 // from the property cell. So the property must be directly on the
1408 // global object.
1409 Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
1410 JSGlobalPropertyCell* cell =
1411 JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
lrn@chromium.org303ada72010-10-27 09:33:13 +00001412 maybe_code = StubCache::ComputeStoreGlobal(*name, *global, cell);
kmillikin@chromium.org69ea3962010-07-05 11:01:40 +00001413 } else {
1414 if (lookup->holder() != *receiver) return;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001415 maybe_code = StubCache::ComputeStoreNormal();
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001416 }
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001417 break;
1418 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001419 case CALLBACKS: {
1420 if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
1421 AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
1422 if (v8::ToCData<Address>(callback->setter()) == 0) return;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001423 maybe_code = StubCache::ComputeStoreCallback(*name, *receiver, callback);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001424 break;
1425 }
1426 case INTERCEPTOR: {
kasperl@chromium.org2abc4502009-07-02 07:00:29 +00001427 ASSERT(!receiver->GetNamedInterceptor()->setter()->IsUndefined());
lrn@chromium.org303ada72010-10-27 09:33:13 +00001428 maybe_code = StubCache::ComputeStoreInterceptor(*name, *receiver);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001429 break;
1430 }
1431 default:
1432 return;
1433 }
1434
1435 // If we're unable to compute the stub (not enough memory left), we
1436 // simply avoid updating the caches.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001437 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001438
1439 // Patch the call site depending on the state of the cache.
1440 if (state == UNINITIALIZED || state == MONOMORPHIC_PROTOTYPE_FAILURE) {
1441 set_target(Code::cast(code));
1442 } else if (state == MONOMORPHIC) {
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001443 // Only move to megamorphic if the target changes.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001444 if (target() != Code::cast(code)) set_target(megamorphic_stub());
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001445 } else if (state == MEGAMORPHIC) {
1446 // Update the stub cache.
1447 StubCache::Set(*name, receiver->map(), Code::cast(code));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001448 }
1449
1450#ifdef DEBUG
1451 TraceIC("StoreIC", name, state, target());
1452#endif
1453}
1454
1455
lrn@chromium.org303ada72010-10-27 09:33:13 +00001456MaybeObject* KeyedStoreIC::Store(State state,
1457 Handle<Object> object,
1458 Handle<Object> key,
1459 Handle<Object> value) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001460 if (key->IsSymbol()) {
1461 Handle<String> name = Handle<String>::cast(key);
1462
1463 // If the object is undefined or null it's illegal to try to set any
1464 // properties on it; throw a TypeError in that case.
1465 if (object->IsUndefined() || object->IsNull()) {
1466 return TypeError("non_object_property_store", object, name);
1467 }
1468
1469 // Ignore stores where the receiver is not a JSObject.
1470 if (!object->IsJSObject()) return *value;
1471 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1472
1473 // Check if the given name is an array index.
1474 uint32_t index;
1475 if (name->AsArrayIndex(&index)) {
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00001476 HandleScope scope;
1477 Handle<Object> result = SetElement(receiver, index, value);
1478 if (result.is_null()) return Failure::Exception();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001479 return *value;
1480 }
1481
1482 // Lookup the property locally in the receiver.
1483 LookupResult lookup;
1484 receiver->LocalLookup(*name, &lookup);
1485
1486 // Update inline cache and stub cache.
kmillikin@chromium.org5d8f0e62010-03-24 08:21:20 +00001487 if (FLAG_use_ic) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001488 UpdateCaches(&lookup, state, receiver, name, value);
1489 }
1490
1491 // Set the property.
1492 return receiver->SetProperty(*name, *value, NONE);
1493 }
1494
1495 // Do not use ICs for objects that require access checks (including
1496 // the global object).
1497 bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001498 ASSERT(!(use_ic && object->IsJSGlobalProxy()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001499
ager@chromium.org3811b432009-10-28 14:53:37 +00001500 if (use_ic) {
1501 Code* stub = generic_stub();
1502 if (object->IsJSObject()) {
1503 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
1504 if (receiver->HasExternalArrayElements()) {
1505 stub = external_array_stub(receiver->GetElementsKind());
1506 }
1507 }
1508 set_target(stub);
1509 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001510
1511 // Set the property.
1512 return Runtime::SetObjectProperty(object, key, value, NONE);
1513}
1514
1515
1516void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
1517 State state,
1518 Handle<JSObject> receiver,
1519 Handle<String> name,
1520 Handle<Object> value) {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001521 // Skip JSGlobalProxy.
1522 if (receiver->IsJSGlobalProxy()) return;
1523
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001524 // Bail out if we didn't find a result.
ager@chromium.org5c838252010-02-19 08:53:10 +00001525 if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001526
1527 // If the property is read-only, we leave the IC in its current
1528 // state.
1529 if (lookup->IsReadOnly()) return;
1530
1531 // If the property has a non-field type allowing map transitions
1532 // where there is extra room in the object, we leave the IC in its
1533 // current state.
1534 PropertyType type = lookup->type();
1535
1536 // Compute the code stub for this store; used for rewriting to
1537 // monomorphic state and making sure that the code stub is in the
1538 // stub cache.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001539 MaybeObject* maybe_code = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001540 Object* code = NULL;
1541
1542 switch (type) {
1543 case FIELD: {
lrn@chromium.org303ada72010-10-27 09:33:13 +00001544 maybe_code = StubCache::ComputeKeyedStoreField(*name, *receiver,
1545 lookup->GetFieldIndex());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001546 break;
1547 }
1548 case MAP_TRANSITION: {
1549 if (lookup->GetAttributes() == NONE) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001550 HandleScope scope;
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001551 ASSERT(type == MAP_TRANSITION);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001552 Handle<Map> transition(lookup->GetTransitionMap());
1553 int index = transition->PropertyIndexFor(*name);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001554 maybe_code = StubCache::ComputeKeyedStoreField(*name, *receiver,
1555 index, *transition);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001556 break;
1557 }
1558 // fall through.
1559 }
1560 default: {
1561 // Always rewrite to the generic case so that we do not
1562 // repeatedly try to rewrite.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001563 maybe_code = generic_stub();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001564 break;
1565 }
1566 }
1567
1568 // If we're unable to compute the stub (not enough memory left), we
1569 // simply avoid updating the caches.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001570 if (maybe_code == NULL || !maybe_code->ToObject(&code)) return;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001571
1572 // Patch the call site depending on the state of the cache. Make
1573 // sure to always rewrite from monomorphic to megamorphic.
1574 ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
1575 if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
1576 set_target(Code::cast(code));
1577 } else if (state == MONOMORPHIC) {
1578 set_target(megamorphic_stub());
1579 }
1580
1581#ifdef DEBUG
1582 TraceIC("KeyedStoreIC", name, state, target());
1583#endif
1584}
1585
1586
1587// ----------------------------------------------------------------------------
1588// Static IC stub generators.
1589//
1590
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +00001591static JSFunction* CompileFunction(JSFunction* function,
1592 InLoopFlag in_loop) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001593 // Compile now with optimization.
1594 HandleScope scope;
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +00001595 Handle<JSFunction> function_handle(function);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001596 if (in_loop == IN_LOOP) {
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +00001597 CompileLazyInLoop(function_handle, CLEAR_EXCEPTION);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001598 } else {
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +00001599 CompileLazy(function_handle, CLEAR_EXCEPTION);
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001600 }
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +00001601 return *function_handle;
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001602}
1603
1604
1605// Used from ic-<arch>.cc.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001606MUST_USE_RESULT MaybeObject* CallIC_Miss(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001607 NoHandleAllocation na;
1608 ASSERT(args.length() == 2);
1609 CallIC ic;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001610 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001611 Object* result;
1612 { MaybeObject* maybe_result =
1613 ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
1614 if (!maybe_result->ToObject(&result)) return maybe_result;
1615 }
ager@chromium.org3bf7b912008-11-17 09:09:45 +00001616
kasperl@chromium.org71affb52009-05-26 05:44:31 +00001617 // The first time the inline cache is updated may be the first time the
1618 // function it references gets called. If the function was lazily compiled
1619 // then the first call will trigger a compilation. We check for this case
1620 // and we do the compilation immediately, instead of waiting for the stub
1621 // currently attached to the JSFunction object to trigger compilation. We
1622 // do this in the case where we know that the inline cache is inside a loop,
1623 // because then we know that we want to optimize the function.
1624 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
1625 return result;
1626 }
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +00001627 return CompileFunction(JSFunction::cast(result), ic.target()->ic_in_loop());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001628}
1629
1630
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001631// Used from ic-<arch>.cc.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001632MUST_USE_RESULT MaybeObject* KeyedCallIC_Miss(Arguments args) {
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001633 NoHandleAllocation na;
1634 ASSERT(args.length() == 2);
1635 KeyedCallIC ic;
1636 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
lrn@chromium.org303ada72010-10-27 09:33:13 +00001637 Object* result;
1638 { MaybeObject* maybe_result =
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001639 ic.LoadFunction(state, args.at<Object>(0), args.at<Object>(1));
lrn@chromium.org303ada72010-10-27 09:33:13 +00001640 if (!maybe_result->ToObject(&result)) return maybe_result;
1641 }
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001642
1643 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
1644 return result;
1645 }
kmillikin@chromium.orgf05f2912010-09-30 10:07:24 +00001646 return CompileFunction(JSFunction::cast(result), ic.target()->ic_in_loop());
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001647}
1648
1649
1650// Used from ic-<arch>.cc.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001651MUST_USE_RESULT MaybeObject* LoadIC_Miss(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001652 NoHandleAllocation na;
1653 ASSERT(args.length() == 2);
1654 LoadIC ic;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001655 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001656 return ic.Load(state, args.at<Object>(0), args.at<String>(1));
1657}
1658
1659
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001660// Used from ic-<arch>.cc
lrn@chromium.org303ada72010-10-27 09:33:13 +00001661MUST_USE_RESULT MaybeObject* KeyedLoadIC_Miss(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001662 NoHandleAllocation na;
1663 ASSERT(args.length() == 2);
1664 KeyedLoadIC ic;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001665 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001666 return ic.Load(state, args.at<Object>(0), args.at<Object>(1));
1667}
1668
1669
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001670// Used from ic-<arch>.cc.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001671MUST_USE_RESULT MaybeObject* StoreIC_Miss(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001672 NoHandleAllocation na;
1673 ASSERT(args.length() == 3);
1674 StoreIC ic;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001675 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001676 return ic.Store(state, args.at<Object>(0), args.at<String>(1),
1677 args.at<Object>(2));
1678}
1679
1680
lrn@chromium.org303ada72010-10-27 09:33:13 +00001681MUST_USE_RESULT MaybeObject* StoreIC_ArrayLength(Arguments args) {
ager@chromium.org5c838252010-02-19 08:53:10 +00001682 NoHandleAllocation nha;
1683
1684 ASSERT(args.length() == 2);
1685 JSObject* receiver = JSObject::cast(args[0]);
1686 Object* len = args[1];
1687
lrn@chromium.org303ada72010-10-27 09:33:13 +00001688 // The generated code should filter out non-Smis before we get here.
1689 ASSERT(len->IsSmi());
1690
1691 Object* result;
1692 { MaybeObject* maybe_result = receiver->SetElementsLength(len);
1693 if (!maybe_result->ToObject(&result)) return maybe_result;
1694 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001695 return len;
ager@chromium.org5c838252010-02-19 08:53:10 +00001696}
1697
1698
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001699// Extend storage is called in a store inline cache when
1700// it is necessary to extend the properties array of a
1701// JSObject.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001702MUST_USE_RESULT MaybeObject* SharedStoreIC_ExtendStorage(Arguments args) {
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001703 NoHandleAllocation na;
1704 ASSERT(args.length() == 3);
1705
1706 // Convert the parameters
1707 JSObject* object = JSObject::cast(args[0]);
1708 Map* transition = Map::cast(args[1]);
1709 Object* value = args[2];
1710
1711 // Check the object has run out out property space.
1712 ASSERT(object->HasFastProperties());
1713 ASSERT(object->map()->unused_property_fields() == 0);
1714
1715 // Expand the properties array.
1716 FixedArray* old_storage = object->properties();
1717 int new_unused = transition->unused_property_fields();
1718 int new_size = old_storage->length() + new_unused + 1;
lrn@chromium.org303ada72010-10-27 09:33:13 +00001719 Object* result;
1720 { MaybeObject* maybe_result = old_storage->CopySize(new_size);
1721 if (!maybe_result->ToObject(&result)) return maybe_result;
1722 }
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001723 FixedArray* new_storage = FixedArray::cast(result);
1724 new_storage->set(old_storage->length(), value);
1725
ager@chromium.org32912102009-01-16 10:38:43 +00001726 // Set the new property value and do the map transition.
kasperl@chromium.org41044eb2008-10-06 08:24:46 +00001727 object->set_properties(new_storage);
1728 object->set_map(transition);
1729
1730 // Return the stored value.
1731 return value;
1732}
1733
1734
lrn@chromium.org1af7e1b2010-06-07 11:12:01 +00001735// Used from ic-<arch>.cc.
lrn@chromium.org303ada72010-10-27 09:33:13 +00001736MUST_USE_RESULT MaybeObject* KeyedStoreIC_Miss(Arguments args) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001737 NoHandleAllocation na;
1738 ASSERT(args.length() == 3);
1739 KeyedStoreIC ic;
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001740 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001741 return ic.Store(state, args.at<Object>(0), args.at<Object>(1),
1742 args.at<Object>(2));
1743}
1744
1745
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001746void BinaryOpIC::patch(Code* code) {
1747 set_target(code);
1748}
1749
1750
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001751const char* BinaryOpIC::GetName(TypeInfo type_info) {
1752 switch (type_info) {
1753 case DEFAULT: return "Default";
1754 case GENERIC: return "Generic";
1755 case HEAP_NUMBERS: return "HeapNumbers";
1756 case STRINGS: return "Strings";
1757 default: return "Invalid";
1758 }
1759}
1760
1761
1762BinaryOpIC::State BinaryOpIC::ToState(TypeInfo type_info) {
1763 switch (type_info) {
1764 // DEFAULT is mapped to UNINITIALIZED so that calls to DEFAULT stubs
1765 // are not cleared at GC.
1766 case DEFAULT: return UNINITIALIZED;
1767
1768 // Could have mapped GENERIC to MONOMORPHIC just as well but MEGAMORPHIC is
1769 // conceptually closer.
1770 case GENERIC: return MEGAMORPHIC;
1771
1772 default: return MONOMORPHIC;
1773 }
1774}
1775
1776
1777BinaryOpIC::TypeInfo BinaryOpIC::GetTypeInfo(Object* left,
1778 Object* right) {
ager@chromium.org357bf652010-04-12 11:30:10 +00001779 if (left->IsSmi() && right->IsSmi()) {
1780 return GENERIC;
1781 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001782
1783 if (left->IsNumber() && right->IsNumber()) {
1784 return HEAP_NUMBERS;
1785 }
1786
1787 if (left->IsString() || right->IsString()) {
1788 // Patching for fast string ADD makes sense even if only one of the
1789 // arguments is a string.
1790 return STRINGS;
1791 }
1792
1793 return GENERIC;
1794}
1795
1796
1797// defined in codegen-<arch>.cc
1798Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info);
1799
1800
lrn@chromium.org303ada72010-10-27 09:33:13 +00001801MUST_USE_RESULT MaybeObject* BinaryOp_Patch(Arguments args) {
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00001802 ASSERT(args.length() == 5);
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001803
1804 Handle<Object> left = args.at<Object>(0);
1805 Handle<Object> right = args.at<Object>(1);
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00001806 int key = Smi::cast(args[2])->value();
1807 Token::Value op = static_cast<Token::Value>(Smi::cast(args[3])->value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001808#ifdef DEBUG
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001809 BinaryOpIC::TypeInfo prev_type_info =
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00001810 static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001811#endif // DEBUG
1812 { HandleScope scope;
1813 BinaryOpIC::TypeInfo type_info = BinaryOpIC::GetTypeInfo(*left, *right);
1814 Handle<Code> code = GetBinaryOpStub(key, type_info);
1815 if (!code.is_null()) {
1816 BinaryOpIC ic;
1817 ic.patch(*code);
1818#ifdef DEBUG
1819 if (FLAG_trace_ic) {
1820 PrintF("[BinaryOpIC (%s->%s)#%s]\n",
1821 BinaryOpIC::GetName(prev_type_info),
1822 BinaryOpIC::GetName(type_info),
1823 Token::Name(op));
1824 }
1825#endif // DEBUG
1826 }
1827 }
1828
erik.corry@gmail.com4a2e25e2010-07-07 12:22:46 +00001829 HandleScope scope;
1830 Handle<JSBuiltinsObject> builtins = Top::builtins();
1831
1832 Object* builtin = NULL; // Initialization calms down the compiler.
1833
1834 switch (op) {
1835 case Token::ADD:
1836 builtin = builtins->javascript_builtin(Builtins::ADD);
1837 break;
1838 case Token::SUB:
1839 builtin = builtins->javascript_builtin(Builtins::SUB);
1840 break;
1841 case Token::MUL:
1842 builtin = builtins->javascript_builtin(Builtins::MUL);
1843 break;
1844 case Token::DIV:
1845 builtin = builtins->javascript_builtin(Builtins::DIV);
1846 break;
1847 case Token::MOD:
1848 builtin = builtins->javascript_builtin(Builtins::MOD);
1849 break;
1850 case Token::BIT_AND:
1851 builtin = builtins->javascript_builtin(Builtins::BIT_AND);
1852 break;
1853 case Token::BIT_OR:
1854 builtin = builtins->javascript_builtin(Builtins::BIT_OR);
1855 break;
1856 case Token::BIT_XOR:
1857 builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
1858 break;
1859 case Token::SHR:
1860 builtin = builtins->javascript_builtin(Builtins::SHR);
1861 break;
1862 case Token::SAR:
1863 builtin = builtins->javascript_builtin(Builtins::SAR);
1864 break;
1865 case Token::SHL:
1866 builtin = builtins->javascript_builtin(Builtins::SHL);
1867 break;
1868 default:
1869 UNREACHABLE();
1870 }
1871
1872 Handle<JSFunction> builtin_function(JSFunction::cast(builtin));
1873
1874 bool caught_exception;
1875 Object** builtin_args[] = { right.location() };
1876 Handle<Object> result = Execution::Call(builtin_function,
1877 left,
1878 ARRAY_SIZE(builtin_args),
1879 builtin_args,
1880 &caught_exception);
1881 if (caught_exception) {
1882 return Failure::Exception();
1883 }
ager@chromium.orgce5e87b2010-03-10 10:24:18 +00001884 return *result;
1885}
1886
1887
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001888static Address IC_utilities[] = {
1889#define ADDR(name) FUNCTION_ADDR(name),
1890 IC_UTIL_LIST(ADDR)
1891 NULL
1892#undef ADDR
1893};
1894
1895
1896Address IC::AddressFromUtilityId(IC::UtilityId id) {
1897 return IC_utilities[id];
1898}
1899
1900
1901} } // namespace v8::internal