summaryrefslogtreecommitdiffstats
path: root/v8/src/ic.cc
diff options
context:
space:
mode:
Diffstat (limited to 'v8/src/ic.cc')
-rw-r--r--v8/src/ic.cc1218
1 files changed, 1218 insertions, 0 deletions
diff --git a/v8/src/ic.cc b/v8/src/ic.cc
new file mode 100644
index 0000000..d7bd764
--- /dev/null
+++ b/v8/src/ic.cc
@@ -0,0 +1,1218 @@
+// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "v8.h"
+
+#include "accessors.h"
+#include "api.h"
+#include "arguments.h"
+#include "execution.h"
+#include "ic-inl.h"
+#include "runtime.h"
+#include "stub-cache.h"
+
+namespace v8 { namespace internal {
+
+#ifdef DEBUG
+static char TransitionMarkFromState(IC::State state) {
+ switch (state) {
+ case UNINITIALIZED: return '0';
+ case UNINITIALIZED_IN_LOOP: return 'L';
+ case PREMONOMORPHIC: return '0';
+ case MONOMORPHIC: return '1';
+ case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
+ case MEGAMORPHIC: return 'N';
+
+ // We never see the debugger states here, because the state is
+ // computed from the original code - not the patched code. Let
+ // these cases fall through to the unreachable code below.
+ case DEBUG_BREAK: break;
+ case DEBUG_PREPARE_STEP_IN: break;
+ }
+ UNREACHABLE();
+ return 0;
+}
+
+void IC::TraceIC(const char* type,
+ Handle<String> name,
+ State old_state,
+ Code* new_target) {
+ if (FLAG_trace_ic) {
+ State new_state = StateFrom(new_target, Heap::undefined_value());
+ PrintF("[%s (%c->%c) ", type,
+ TransitionMarkFromState(old_state),
+ TransitionMarkFromState(new_state));
+ name->Print();
+ PrintF("]\n");
+ }
+}
+#endif
+
+
+IC::IC(FrameDepth depth) {
+ // To improve the performance of the (much used) IC code, we unfold
+ // a few levels of the stack frame iteration code. This yields a
+ // ~35% speedup when running DeltaBlue with the '--nouse-ic' flag.
+ const Address entry = Top::c_entry_fp(Top::GetCurrentThread());
+ Address* pc_address =
+ reinterpret_cast<Address*>(entry + ExitFrameConstants::kCallerPCOffset);
+ Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
+ // If there's another JavaScript frame on the stack, we need to look
+ // one frame further down the stack to find the frame pointer and
+ // the return address stack slot.
+ if (depth == EXTRA_CALL_FRAME) {
+ const int kCallerPCOffset = StandardFrameConstants::kCallerPCOffset;
+ pc_address = reinterpret_cast<Address*>(fp + kCallerPCOffset);
+ fp = Memory::Address_at(fp + StandardFrameConstants::kCallerFPOffset);
+ }
+#ifdef DEBUG
+ StackFrameIterator it;
+ for (int i = 0; i < depth + 1; i++) it.Advance();
+ StackFrame* frame = it.frame();
+ ASSERT(fp == frame->fp() && pc_address == frame->pc_address());
+#endif
+ fp_ = fp;
+ pc_address_ = pc_address;
+}
+
+
+Address IC::OriginalCodeAddress() {
+ HandleScope scope;
+ // Compute the JavaScript frame for the frame pointer of this IC
+ // structure. We need this to be able to find the function
+ // corresponding to the frame.
+ StackFrameIterator it;
+ while (it.frame()->fp() != this->fp()) it.Advance();
+ JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame());
+ // Find the function on the stack and both the active code for the
+ // function and the original code.
+ JSFunction* function = JSFunction::cast(frame->function());
+ Handle<SharedFunctionInfo> shared(function->shared());
+ Code* code = shared->code();
+ ASSERT(Debug::HasDebugInfo(shared));
+ Code* original_code = Debug::GetDebugInfo(shared)->original_code();
+ ASSERT(original_code->IsCode());
+ // Get the address of the call site in the active code. This is the
+ // place where the call to DebugBreakXXX is and where the IC
+ // normally would be.
+ Address addr = pc() - Assembler::kTargetAddrToReturnAddrDist;
+ // Return the address in the original code. This is the place where
+ // the call which has been overwritten by the DebugBreakXXX resides
+ // and the place where the inline cache system should look.
+ int delta = original_code->instruction_start() - code->instruction_start();
+ return addr + delta;
+}
+
+
+IC::State IC::StateFrom(Code* target, Object* receiver) {
+ IC::State state = target->ic_state();
+
+ if (state != MONOMORPHIC) return state;
+ if (receiver->IsUndefined() || receiver->IsNull()) return state;
+
+ Map* map = GetCodeCacheMapForObject(receiver);
+
+ // Decide whether the inline cache failed because of changes to the
+ // receiver itself or changes to one of its prototypes.
+ //
+ // If there are changes to the receiver itself, the map of the
+ // receiver will have changed and the current target will not be in
+ // the receiver map's code cache. Therefore, if the current target
+ // is in the receiver map's code cache, the inline cache failed due
+ // to prototype check failure.
+ int index = map->IndexInCodeCache(target);
+ if (index >= 0) {
+ // For keyed load/store, the most likely cause of cache failure is
+ // that the key has changed. We do not distinguish between
+ // prototype and non-prototype failures for keyed access.
+ Code::Kind kind = target->kind();
+ if (kind == Code::KEYED_LOAD_IC || kind == Code::KEYED_STORE_IC) {
+ return MONOMORPHIC;
+ }
+
+ // Remove the target from the code cache to avoid hitting the same
+ // invalid stub again.
+ map->RemoveFromCodeCache(index);
+
+ return MONOMORPHIC_PROTOTYPE_FAILURE;
+ }
+
+ // The builtins object is special. It only changes when JavaScript
+ // builtins are loaded lazily. It is important to keep inline
+ // caches for the builtins object monomorphic. Therefore, if we get
+ // an inline cache miss for the builtins object after lazily loading
+ // JavaScript builtins, we return uninitialized as the state to
+ // force the inline cache back to monomorphic state.
+ if (receiver->IsJSBuiltinsObject()) {
+ return UNINITIALIZED;
+ }
+
+ return MONOMORPHIC;
+}
+
+
+RelocInfo::Mode IC::ComputeMode() {
+ Address addr = address();
+ Code* code = Code::cast(Heap::FindCodeObject(addr));
+ for (RelocIterator it(code, RelocInfo::kCodeTargetMask);
+ !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ if (info->pc() == addr) return info->rmode();
+ }
+ UNREACHABLE();
+ return RelocInfo::NONE;
+}
+
+
+Failure* IC::TypeError(const char* type,
+ Handle<Object> object,
+ Handle<String> name) {
+ HandleScope scope;
+ Handle<Object> args[2] = { name, object };
+ Handle<Object> error = Factory::NewTypeError(type, HandleVector(args, 2));
+ return Top::Throw(*error);
+}
+
+
+Failure* IC::ReferenceError(const char* type, Handle<String> name) {
+ HandleScope scope;
+ Handle<Object> error =
+ Factory::NewReferenceError(type, HandleVector(&name, 1));
+ return Top::Throw(*error);
+}
+
+
+void IC::Clear(Address address) {
+ Code* target = GetTargetAtAddress(address);
+
+ // Don't clear debug break inline cache as it will remove the break point.
+ if (target->ic_state() == DEBUG_BREAK) return;
+
+ switch (target->kind()) {
+ case Code::LOAD_IC: return LoadIC::Clear(address, target);
+ case Code::KEYED_LOAD_IC: return KeyedLoadIC::Clear(address, target);
+ case Code::STORE_IC: return StoreIC::Clear(address, target);
+ case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target);
+ case Code::CALL_IC: return CallIC::Clear(address, target);
+ default: UNREACHABLE();
+ }
+}
+
+
+void CallIC::Clear(Address address, Code* target) {
+ State state = target->ic_state();
+ if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP) return;
+ Code* code = StubCache::FindCallInitialize(target->arguments_count());
+ SetTargetAtAddress(address, code);
+}
+
+
+void KeyedLoadIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ // Make sure to also clear the map used in inline fast cases. If we
+ // do not clear these maps, cached code can keep objects alive
+ // through the embedded maps.
+ PatchInlinedMapCheck(address, Heap::null_value());
+ SetTargetAtAddress(address, initialize_stub());
+}
+
+
+void LoadIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ SetTargetAtAddress(address, initialize_stub());
+}
+
+
+void StoreIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ SetTargetAtAddress(address, initialize_stub());
+}
+
+
+void KeyedStoreIC::Clear(Address address, Code* target) {
+ if (target->ic_state() == UNINITIALIZED) return;
+ SetTargetAtAddress(address, initialize_stub());
+}
+
+
+Object* CallIC::TryCallAsFunction(Object* object) {
+ HandleScope scope;
+ Handle<Object> target(object);
+ Handle<Object> delegate = Execution::GetFunctionDelegate(target);
+
+ if (delegate->IsJSFunction()) {
+ // Patch the receiver and use the delegate as the function to
+ // invoke. This is used for invoking objects as if they were
+ // functions.
+ const int argc = this->target()->arguments_count();
+ StackFrameLocator locator;
+ JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
+ int index = frame->ComputeExpressionsCount() - (argc + 1);
+ frame->SetExpression(index, *target);
+ }
+
+ return *delegate;
+}
+
+
+Object* CallIC::LoadFunction(State state,
+ Handle<Object> object,
+ Handle<String> name) {
+ // If the object is undefined or null it's illegal to try to get any
+ // of its properties; throw a TypeError in that case.
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_call", object, name);
+ }
+
+ Object* result = Heap::the_hole_value();
+
+ // Check if the name is trivially convertible to an index and get
+ // the element if so.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ result = object->GetElement(index);
+ if (result->IsJSFunction()) return result;
+
+ // Try to find a suitable function delegate for the object at hand.
+ result = TryCallAsFunction(result);
+ if (result->IsJSFunction()) return result;
+
+ // Otherwise, it will fail in the lookup step.
+ }
+
+ // Lookup the property in the object.
+ LookupResult lookup;
+ object->Lookup(*name, &lookup);
+
+ if (!lookup.IsValid()) {
+ // If the object does not have the requested property, check which
+ // exception we need to throw.
+ if (is_contextual()) {
+ return ReferenceError("not_defined", name);
+ }
+ return TypeError("undefined_method", object, name);
+ }
+
+ // Lookup is valid: Update inline cache and stub cache.
+ if (FLAG_use_ic && lookup.IsLoaded()) {
+ UpdateCaches(&lookup, state, object, name);
+ }
+
+ if (lookup.type() == INTERCEPTOR) {
+ // Get the property.
+ PropertyAttributes attr;
+ result = object->GetProperty(*name, &attr);
+ if (result->IsFailure()) return result;
+ // If the object does not have the requested property, check which
+ // exception we need to throw.
+ if (attr == ABSENT) {
+ if (is_contextual()) {
+ return ReferenceError("not_defined", name);
+ }
+ return TypeError("undefined_method", object, name);
+ }
+ } else {
+ // Lookup is valid and no interceptors are involved. Get the
+ // property.
+ result = object->GetProperty(*name);
+ if (result->IsFailure()) return result;
+ }
+
+ ASSERT(result != Heap::the_hole_value());
+
+ if (result->IsJSFunction()) {
+ // Check if there is an optimized (builtin) version of the function.
+ // Ignored this will degrade performance for Array.prototype.{push,pop}.
+ // Please note we only return the optimized function iff
+ // the JSObject has FastElements.
+ if (object->IsJSObject() && JSObject::cast(*object)->HasFastElements()) {
+ Object* opt = Top::LookupSpecialFunction(JSObject::cast(*object),
+ lookup.holder(),
+ JSFunction::cast(result));
+ if (opt->IsJSFunction()) return opt;
+ }
+
+ // Handle stepping into a function if step into is active.
+ if (Debug::StepInActive()) {
+ // Protect the result in a handle as the debugger can allocate and might
+ // cause GC.
+ HandleScope scope;
+ Handle<JSFunction> function(JSFunction::cast(result));
+ Debug::HandleStepIn(function, fp(), false);
+ return *function;
+ }
+
+ return result;
+ }
+
+ // Try to find a suitable function delegate for the object at hand.
+ result = TryCallAsFunction(result);
+ return result->IsJSFunction() ?
+ result : TypeError("property_not_function", object, name);
+}
+
+
+void CallIC::UpdateCaches(LookupResult* lookup,
+ State state,
+ Handle<Object> object,
+ Handle<String> name) {
+ ASSERT(lookup->IsLoaded());
+ // Bail out if we didn't find a result.
+ if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+
+ // Compute the number of arguments.
+ int argc = target()->arguments_count();
+ Object* code = NULL;
+
+ if (state == UNINITIALIZED) {
+ // This is the first time we execute this inline cache.
+ // Set the target to the pre monomorphic stub to delay
+ // setting the monomorphic state.
+ code = StubCache::ComputeCallPreMonomorphic(argc);
+ } else if (state == MONOMORPHIC) {
+ code = StubCache::ComputeCallMegamorphic(argc);
+ } else {
+ // Compute monomorphic stub.
+ switch (lookup->type()) {
+ case FIELD: {
+ int index = lookup->GetFieldIndex();
+ code = StubCache::ComputeCallField(argc, *name, *object,
+ lookup->holder(), index);
+ break;
+ }
+ case CONSTANT_FUNCTION: {
+ // Get the constant function and compute the code stub for this
+ // call; used for rewriting to monomorphic state and making sure
+ // that the code stub is in the stub cache.
+ JSFunction* function = lookup->GetConstantFunction();
+ code = StubCache::ComputeCallConstant(argc, *name, *object,
+ lookup->holder(), function);
+ break;
+ }
+ case NORMAL: {
+ // There is only one shared stub for calling normalized
+ // properties. It does not traverse the prototype chain, so the
+ // property must be found in the receiver for the stub to be
+ // applicable.
+ if (!object->IsJSObject()) return;
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (lookup->holder() != *receiver) return;
+ code = StubCache::ComputeCallNormal(argc, *name, *receiver);
+ break;
+ }
+ case INTERCEPTOR: {
+ code = StubCache::ComputeCallInterceptor(argc, *name, *object,
+ lookup->holder());
+ break;
+ }
+ default:
+ return;
+ }
+ }
+
+ // If we're unable to compute the stub (not enough memory left), we
+ // simply avoid updating the caches.
+ if (code->IsFailure()) return;
+
+ // Patch the call site depending on the state of the cache.
+ if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP ||
+ state == PREMONOMORPHIC || state == MONOMORPHIC ||
+ state == MONOMORPHIC_PROTOTYPE_FAILURE) {
+ set_target(Code::cast(code));
+ }
+
+#ifdef DEBUG
+ TraceIC("CallIC", name, state, target());
+#endif
+}
+
+
+Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
+ // If the object is undefined or null it's illegal to try to get any
+ // of its properties; throw a TypeError in that case.
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_load", object, name);
+ }
+
+ if (FLAG_use_ic) {
+ // Use specialized code for getting the length of strings and
+ // string wrapper objects. The length property of string wrapper
+ // objects is read-only and therefore always returns the length of
+ // the underlying string value. See ECMA-262 15.5.5.1.
+ if ((object->IsString() || object->IsStringWrapper()) &&
+ name->Equals(Heap::length_symbol())) {
+ HandleScope scope;
+ // Get the string if we have a string wrapper object.
+ if (object->IsJSValue()) {
+ object = Handle<Object>(Handle<JSValue>::cast(object)->value());
+ }
+#ifdef DEBUG
+ if (FLAG_trace_ic) PrintF("[LoadIC : +#length /string]\n");
+#endif
+ Code* target = NULL;
+ target = Builtins::builtin(Builtins::LoadIC_StringLength);
+ set_target(target);
+ StubCache::Set(*name, HeapObject::cast(*object)->map(), target);
+ return Smi::FromInt(String::cast(*object)->length());
+ }
+
+ // Use specialized code for getting the length of arrays.
+ if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
+#ifdef DEBUG
+ if (FLAG_trace_ic) PrintF("[LoadIC : +#length /array]\n");
+#endif
+ Code* target = Builtins::builtin(Builtins::LoadIC_ArrayLength);
+ set_target(target);
+ StubCache::Set(*name, HeapObject::cast(*object)->map(), target);
+ return JSArray::cast(*object)->length();
+ }
+
+ // Use specialized code for getting prototype of functions.
+ if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol())) {
+#ifdef DEBUG
+ if (FLAG_trace_ic) PrintF("[LoadIC : +#prototype /function]\n");
+#endif
+ Code* target = Builtins::builtin(Builtins::LoadIC_FunctionPrototype);
+ set_target(target);
+ StubCache::Set(*name, HeapObject::cast(*object)->map(), target);
+ return Accessors::FunctionGetPrototype(*object, 0);
+ }
+ }
+
+ // Check if the name is trivially convertible to an index and get
+ // the element if so.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) return object->GetElement(index);
+
+ // Named lookup in the object.
+ LookupResult lookup;
+ object->Lookup(*name, &lookup);
+
+ // If lookup is invalid, check if we need to throw an exception.
+ if (!lookup.IsValid()) {
+ if (FLAG_strict || is_contextual()) {
+ return ReferenceError("not_defined", name);
+ }
+ LOG(SuspectReadEvent(*name, *object));
+ }
+
+ // Update inline cache and stub cache.
+ if (FLAG_use_ic && lookup.IsLoaded()) {
+ UpdateCaches(&lookup, state, object, name);
+ }
+
+ PropertyAttributes attr;
+ if (lookup.IsValid() && lookup.type() == INTERCEPTOR) {
+ // Get the property.
+ Object* result = object->GetProperty(*object, &lookup, *name, &attr);
+ if (result->IsFailure()) return result;
+ // If the property is not present, check if we need to throw an
+ // exception.
+ if (attr == ABSENT && is_contextual()) {
+ return ReferenceError("not_defined", name);
+ }
+ return result;
+ }
+
+ // Get the property.
+ return object->GetProperty(*object, &lookup, *name, &attr);
+}
+
+
+void LoadIC::UpdateCaches(LookupResult* lookup,
+ State state,
+ Handle<Object> object,
+ Handle<String> name) {
+ ASSERT(lookup->IsLoaded());
+ // Bail out if we didn't find a result.
+ if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+
+ // Loading properties from values is not common, so don't try to
+ // deal with non-JS objects here.
+ if (!object->IsJSObject()) return;
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+
+ // Compute the code stub for this load.
+ Object* code = NULL;
+ if (state == UNINITIALIZED) {
+ // This is the first time we execute this inline cache.
+ // Set the target to the pre monomorphic stub to delay
+ // setting the monomorphic state.
+ code = pre_monomorphic_stub();
+ } else {
+ // Compute monomorphic stub.
+ switch (lookup->type()) {
+ case FIELD: {
+ code = StubCache::ComputeLoadField(*name, *receiver,
+ lookup->holder(),
+ lookup->GetFieldIndex());
+ break;
+ }
+ case CONSTANT_FUNCTION: {
+ Object* constant = lookup->GetConstantFunction();
+ code = StubCache::ComputeLoadConstant(*name, *receiver,
+ lookup->holder(), constant);
+ break;
+ }
+ case NORMAL: {
+ // There is only one shared stub for loading normalized
+ // properties. It does not traverse the prototype chain, so the
+ // property must be found in the receiver for the stub to be
+ // applicable.
+ if (lookup->holder() != *receiver) return;
+ code = StubCache::ComputeLoadNormal(*name, *receiver);
+ break;
+ }
+ case CALLBACKS: {
+ if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
+ AccessorInfo* callback =
+ AccessorInfo::cast(lookup->GetCallbackObject());
+ if (v8::ToCData<Address>(callback->getter()) == 0) return;
+ code = StubCache::ComputeLoadCallback(*name, *receiver,
+ lookup->holder(), callback);
+ break;
+ }
+ case INTERCEPTOR: {
+ code = StubCache::ComputeLoadInterceptor(*name, *receiver,
+ lookup->holder());
+ break;
+ }
+ default:
+ return;
+ }
+ }
+
+ // If we're unable to compute the stub (not enough memory left), we
+ // simply avoid updating the caches.
+ if (code->IsFailure()) return;
+
+ // Patch the call site depending on the state of the cache.
+ if (state == UNINITIALIZED || state == PREMONOMORPHIC ||
+ state == MONOMORPHIC_PROTOTYPE_FAILURE) {
+ set_target(Code::cast(code));
+ } else if (state == MONOMORPHIC) {
+ set_target(megamorphic_stub());
+ }
+
+#ifdef DEBUG
+ TraceIC("LoadIC", name, state, target());
+#endif
+}
+
+
+Object* KeyedLoadIC::Load(State state,
+ Handle<Object> object,
+ Handle<Object> key) {
+ if (key->IsSymbol()) {
+ Handle<String> name = Handle<String>::cast(key);
+
+ // If the object is undefined or null it's illegal to try to get any
+ // of its properties; throw a TypeError in that case.
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_load", object, name);
+ }
+
+ if (FLAG_use_ic) {
+ // Use specialized code for getting the length of strings.
+ if (object->IsString() && name->Equals(Heap::length_symbol())) {
+ Handle<String> string = Handle<String>::cast(object);
+ Object* code = NULL;
+ code = StubCache::ComputeKeyedLoadStringLength(*name, *string);
+ if (code->IsFailure()) return code;
+ set_target(Code::cast(code));
+#ifdef DEBUG
+ TraceIC("KeyedLoadIC", name, state, target());
+#endif
+ return Smi::FromInt(string->length());
+ }
+
+ // Use specialized code for getting the length of arrays.
+ if (object->IsJSArray() && name->Equals(Heap::length_symbol())) {
+ Handle<JSArray> array = Handle<JSArray>::cast(object);
+ Object* code = StubCache::ComputeKeyedLoadArrayLength(*name, *array);
+ if (code->IsFailure()) return code;
+ set_target(Code::cast(code));
+#ifdef DEBUG
+ TraceIC("KeyedLoadIC", name, state, target());
+#endif
+ return JSArray::cast(*object)->length();
+ }
+
+ // Use specialized code for getting prototype of functions.
+ if (object->IsJSFunction() && name->Equals(Heap::prototype_symbol())) {
+ Handle<JSFunction> function = Handle<JSFunction>::cast(object);
+ Object* code =
+ StubCache::ComputeKeyedLoadFunctionPrototype(*name, *function);
+ if (code->IsFailure()) return code;
+ set_target(Code::cast(code));
+#ifdef DEBUG
+ TraceIC("KeyedLoadIC", name, state, target());
+#endif
+ return Accessors::FunctionGetPrototype(*object, 0);
+ }
+ }
+
+ // Check if the name is trivially convertible to an index and get
+ // the element or char if so.
+ uint32_t index = 0;
+ if (name->AsArrayIndex(&index)) {
+ HandleScope scope;
+ // Rewrite to the generic keyed load stub.
+ if (FLAG_use_ic) set_target(generic_stub());
+ return Runtime::GetElementOrCharAt(object, index);
+ }
+
+ // Named lookup.
+ LookupResult lookup;
+ object->Lookup(*name, &lookup);
+
+ // If lookup is invalid, check if we need to throw an exception.
+ if (!lookup.IsValid()) {
+ if (FLAG_strict || is_contextual()) {
+ return ReferenceError("not_defined", name);
+ }
+ }
+
+ // Update the inline cache.
+ if (FLAG_use_ic && lookup.IsLoaded()) {
+ UpdateCaches(&lookup, state, object, name);
+ }
+
+ PropertyAttributes attr;
+ if (lookup.IsValid() && lookup.type() == INTERCEPTOR) {
+ // Get the property.
+ Object* result = object->GetProperty(*object, &lookup, *name, &attr);
+ if (result->IsFailure()) return result;
+ // If the property is not present, check if we need to throw an
+ // exception.
+ if (attr == ABSENT && is_contextual()) {
+ return ReferenceError("not_defined", name);
+ }
+ return result;
+ }
+
+ return object->GetProperty(*object, &lookup, *name, &attr);
+ }
+
+ // Do not use ICs for objects that require access checks (including
+ // the global object).
+ bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
+
+ if (use_ic) {
+ set_target(generic_stub());
+ // For JSObjects that are not value wrappers and that do not have
+ // indexed interceptors, we initialize the inlined fast case (if
+ // present) by patching the inlined map check.
+ if (object->IsJSObject() &&
+ !object->IsJSValue() &&
+ !JSObject::cast(*object)->HasIndexedInterceptor()) {
+ Map* map = JSObject::cast(*object)->map();
+ PatchInlinedMapCheck(address(), map);
+ }
+ }
+
+ // Get the property.
+ return Runtime::GetObjectProperty(object, key);
+}
+
+
+void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
+ Handle<Object> object, Handle<String> name) {
+ ASSERT(lookup->IsLoaded());
+ // Bail out if we didn't find a result.
+ if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+
+ if (!object->IsJSObject()) return;
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+
+ // Compute the code stub for this load.
+ Object* code = NULL;
+
+ if (state == UNINITIALIZED) {
+ // This is the first time we execute this inline cache.
+ // Set the target to the pre monomorphic stub to delay
+ // setting the monomorphic state.
+ code = pre_monomorphic_stub();
+ } else {
+ // Compute a monomorphic stub.
+ switch (lookup->type()) {
+ case FIELD: {
+ code = StubCache::ComputeKeyedLoadField(*name, *receiver,
+ lookup->holder(),
+ lookup->GetFieldIndex());
+ break;
+ }
+ case CONSTANT_FUNCTION: {
+ Object* constant = lookup->GetConstantFunction();
+ code = StubCache::ComputeKeyedLoadConstant(*name, *receiver,
+ lookup->holder(), constant);
+ break;
+ }
+ case CALLBACKS: {
+ if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
+ AccessorInfo* callback =
+ AccessorInfo::cast(lookup->GetCallbackObject());
+ if (v8::ToCData<Address>(callback->getter()) == 0) return;
+ code = StubCache::ComputeKeyedLoadCallback(*name, *receiver,
+ lookup->holder(), callback);
+ break;
+ }
+ case INTERCEPTOR: {
+ code = StubCache::ComputeKeyedLoadInterceptor(*name, *receiver,
+ lookup->holder());
+ break;
+ }
+ default: {
+ // Always rewrite to the generic case so that we do not
+ // repeatedly try to rewrite.
+ code = generic_stub();
+ break;
+ }
+ }
+ }
+
+ // If we're unable to compute the stub (not enough memory left), we
+ // simply avoid updating the caches.
+ if (code->IsFailure()) return;
+
+ // Patch the call site depending on the state of the cache. Make
+ // sure to always rewrite from monomorphic to megamorphic.
+ ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
+ if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
+ set_target(Code::cast(code));
+ } else if (state == MONOMORPHIC) {
+ set_target(megamorphic_stub());
+ }
+
+#ifdef DEBUG
+ TraceIC("KeyedLoadIC", name, state, target());
+#endif
+}
+
+
+Object* StoreIC::Store(State state,
+ Handle<Object> object,
+ Handle<String> name,
+ Handle<Object> value) {
+ // If the object is undefined or null it's illegal to try to set any
+ // properties on it; throw a TypeError in that case.
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_store", object, name);
+ }
+
+ // Ignore stores where the receiver is not a JSObject.
+ if (!object->IsJSObject()) return *value;
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+
+ // Check if the given name is an array index.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ HandleScope scope;
+ Handle<Object> result = SetElement(receiver, index, value);
+ if (result.is_null()) return Failure::Exception();
+ return *value;
+ }
+
+ // Lookup the property locally in the receiver.
+ LookupResult lookup;
+ receiver->LocalLookup(*name, &lookup);
+
+ // Update inline cache and stub cache.
+ if (FLAG_use_ic && lookup.IsLoaded()) {
+ UpdateCaches(&lookup, state, receiver, name, value);
+ }
+
+ // Set the property.
+ return receiver->SetProperty(*name, *value, NONE);
+}
+
+
+void StoreIC::UpdateCaches(LookupResult* lookup,
+ State state,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value) {
+ ASSERT(lookup->IsLoaded());
+ // Skip JSGlobalProxy.
+ if (receiver->IsJSGlobalProxy()) return;
+
+ // Bail out if we didn't find a result.
+ if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+
+ // If the property is read-only, we leave the IC in its current
+ // state.
+ if (lookup->IsReadOnly()) return;
+
+ // If the property has a non-field type allowing map transitions
+ // where there is extra room in the object, we leave the IC in its
+ // current state.
+ PropertyType type = lookup->type();
+
+ // Compute the code stub for this store; used for rewriting to
+ // monomorphic state and making sure that the code stub is in the
+ // stub cache.
+ Object* code = NULL;
+ switch (type) {
+ case FIELD: {
+ code = StubCache::ComputeStoreField(*name, *receiver,
+ lookup->GetFieldIndex());
+ break;
+ }
+ case MAP_TRANSITION: {
+ if (lookup->GetAttributes() != NONE) return;
+ HandleScope scope;
+ ASSERT(type == MAP_TRANSITION);
+ Handle<Map> transition(lookup->GetTransitionMap());
+ int index = transition->PropertyIndexFor(*name);
+ code = StubCache::ComputeStoreField(*name, *receiver, index, *transition);
+ break;
+ }
+ case CALLBACKS: {
+ if (!lookup->GetCallbackObject()->IsAccessorInfo()) return;
+ AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
+ if (v8::ToCData<Address>(callback->setter()) == 0) return;
+ code = StubCache::ComputeStoreCallback(*name, *receiver, callback);
+ break;
+ }
+ case INTERCEPTOR: {
+ code = StubCache::ComputeStoreInterceptor(*name, *receiver);
+ break;
+ }
+ default:
+ return;
+ }
+
+ // If we're unable to compute the stub (not enough memory left), we
+ // simply avoid updating the caches.
+ if (code->IsFailure()) return;
+
+ // Patch the call site depending on the state of the cache.
+ if (state == UNINITIALIZED || state == MONOMORPHIC_PROTOTYPE_FAILURE) {
+ set_target(Code::cast(code));
+ } else if (state == MONOMORPHIC) {
+ // Only move to mega morphic if the target changes.
+ if (target() != Code::cast(code)) set_target(megamorphic_stub());
+ }
+
+#ifdef DEBUG
+ TraceIC("StoreIC", name, state, target());
+#endif
+}
+
+
+Object* KeyedStoreIC::Store(State state,
+ Handle<Object> object,
+ Handle<Object> key,
+ Handle<Object> value) {
+ if (key->IsSymbol()) {
+ Handle<String> name = Handle<String>::cast(key);
+
+ // If the object is undefined or null it's illegal to try to set any
+ // properties on it; throw a TypeError in that case.
+ if (object->IsUndefined() || object->IsNull()) {
+ return TypeError("non_object_property_store", object, name);
+ }
+
+ // Ignore stores where the receiver is not a JSObject.
+ if (!object->IsJSObject()) return *value;
+ Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+
+ // Check if the given name is an array index.
+ uint32_t index;
+ if (name->AsArrayIndex(&index)) {
+ HandleScope scope;
+ Handle<Object> result = SetElement(receiver, index, value);
+ if (result.is_null()) return Failure::Exception();
+ return *value;
+ }
+
+ // Lookup the property locally in the receiver.
+ LookupResult lookup;
+ receiver->LocalLookup(*name, &lookup);
+
+ // Update inline cache and stub cache.
+ if (FLAG_use_ic && lookup.IsLoaded()) {
+ UpdateCaches(&lookup, state, receiver, name, value);
+ }
+
+ // Set the property.
+ return receiver->SetProperty(*name, *value, NONE);
+ }
+
+ // Do not use ICs for objects that require access checks (including
+ // the global object).
+ bool use_ic = FLAG_use_ic && !object->IsAccessCheckNeeded();
+ ASSERT(!(use_ic && object->IsJSGlobalProxy()));
+
+ if (use_ic) set_target(generic_stub());
+
+ // Set the property.
+ return Runtime::SetObjectProperty(object, key, value, NONE);
+}
+
+
+void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
+ State state,
+ Handle<JSObject> receiver,
+ Handle<String> name,
+ Handle<Object> value) {
+ ASSERT(lookup->IsLoaded());
+
+ // Skip JSGlobalProxy.
+ if (receiver->IsJSGlobalProxy()) return;
+
+ // Bail out if we didn't find a result.
+ if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+
+ // If the property is read-only, we leave the IC in its current
+ // state.
+ if (lookup->IsReadOnly()) return;
+
+ // If the property has a non-field type allowing map transitions
+ // where there is extra room in the object, we leave the IC in its
+ // current state.
+ PropertyType type = lookup->type();
+
+ // Compute the code stub for this store; used for rewriting to
+ // monomorphic state and making sure that the code stub is in the
+ // stub cache.
+ Object* code = NULL;
+
+ switch (type) {
+ case FIELD: {
+ code = StubCache::ComputeKeyedStoreField(*name, *receiver,
+ lookup->GetFieldIndex());
+ break;
+ }
+ case MAP_TRANSITION: {
+ if (lookup->GetAttributes() == NONE) {
+ HandleScope scope;
+ ASSERT(type == MAP_TRANSITION);
+ Handle<Map> transition(lookup->GetTransitionMap());
+ int index = transition->PropertyIndexFor(*name);
+ code = StubCache::ComputeKeyedStoreField(*name, *receiver,
+ index, *transition);
+ break;
+ }
+ // fall through.
+ }
+ default: {
+ // Always rewrite to the generic case so that we do not
+ // repeatedly try to rewrite.
+ code = generic_stub();
+ break;
+ }
+ }
+
+ // If we're unable to compute the stub (not enough memory left), we
+ // simply avoid updating the caches.
+ if (code->IsFailure()) return;
+
+ // Patch the call site depending on the state of the cache. Make
+ // sure to always rewrite from monomorphic to megamorphic.
+ ASSERT(state != MONOMORPHIC_PROTOTYPE_FAILURE);
+ if (state == UNINITIALIZED || state == PREMONOMORPHIC) {
+ set_target(Code::cast(code));
+ } else if (state == MONOMORPHIC) {
+ set_target(megamorphic_stub());
+ }
+
+#ifdef DEBUG
+ TraceIC("KeyedStoreIC", name, state, target());
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+// Static IC stub generators.
+//
+
+// Used from ic_<arch>.cc.
+Object* CallIC_Miss(Arguments args) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 2);
+ CallIC ic;
+ IC::State state = IC::StateFrom(ic.target(), args[0]);
+ Object* result =
+ ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
+ if (state != UNINITIALIZED_IN_LOOP || !result->IsJSFunction())
+ return result;
+
+ // Compile the function with the knowledge that it's called from
+ // within a loop. This enables further optimization of the function.
+ HandleScope scope;
+ Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result));
+ if (!function->is_compiled()) CompileLazyInLoop(function, CLEAR_EXCEPTION);
+ return *function;
+}
+
+
+void CallIC::GenerateInitialize(MacroAssembler* masm, int argc) {
+ Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss)));
+}
+
+
+void CallIC::GeneratePreMonomorphic(MacroAssembler* masm, int argc) {
+ Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss)));
+}
+
+
+void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+ Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss)));
+}
+
+
+// Used from ic_<arch>.cc.
+Object* LoadIC_Miss(Arguments args) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 2);
+ LoadIC ic;
+ IC::State state = IC::StateFrom(ic.target(), args[0]);
+ return ic.Load(state, args.at<Object>(0), args.at<String>(1));
+}
+
+
+void LoadIC::GenerateInitialize(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
+}
+
+
+void LoadIC::GeneratePreMonomorphic(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
+}
+
+
+// Used from ic_<arch>.cc
+Object* KeyedLoadIC_Miss(Arguments args) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 2);
+ KeyedLoadIC ic;
+ IC::State state = IC::StateFrom(ic.target(), args[0]);
+ return ic.Load(state, args.at<Object>(0), args.at<Object>(1));
+}
+
+
+void KeyedLoadIC::GenerateInitialize(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
+}
+
+
+void KeyedLoadIC::GeneratePreMonomorphic(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
+}
+
+
+// Used from ic_<arch>.cc.
+Object* StoreIC_Miss(Arguments args) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 3);
+ StoreIC ic;
+ IC::State state = IC::StateFrom(ic.target(), args[0]);
+ return ic.Store(state, args.at<Object>(0), args.at<String>(1),
+ args.at<Object>(2));
+}
+
+
+// Extend storage is called in a store inline cache when
+// it is necessary to extend the properties array of a
+// JSObject.
+Object* SharedStoreIC_ExtendStorage(Arguments args) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 3);
+
+ // Convert the parameters
+ JSObject* object = JSObject::cast(args[0]);
+ Map* transition = Map::cast(args[1]);
+ Object* value = args[2];
+
+ // Check the object has run out out property space.
+ ASSERT(object->HasFastProperties());
+ ASSERT(object->map()->unused_property_fields() == 0);
+
+ // Expand the properties array.
+ FixedArray* old_storage = object->properties();
+ int new_unused = transition->unused_property_fields();
+ int new_size = old_storage->length() + new_unused + 1;
+ Object* result = old_storage->CopySize(new_size);
+ if (result->IsFailure()) return result;
+ FixedArray* new_storage = FixedArray::cast(result);
+ new_storage->set(old_storage->length(), value);
+
+ // Set the new property value and do the map transition.
+ object->set_properties(new_storage);
+ object->set_map(transition);
+
+ // Return the stored value.
+ return value;
+}
+
+
+void StoreIC::GenerateInitialize(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kStoreIC_Miss)));
+}
+
+
+void StoreIC::GenerateMiss(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kStoreIC_Miss)));
+}
+
+
+// Used from ic_<arch>.cc.
+Object* KeyedStoreIC_Miss(Arguments args) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic;
+ IC::State state = IC::StateFrom(ic.target(), args[0]);
+ return ic.Store(state, args.at<Object>(0), args.at<Object>(1),
+ args.at<Object>(2));
+}
+
+
+void KeyedStoreIC::GenerateInitialize(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kKeyedStoreIC_Miss)));
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
+ Generate(masm, ExternalReference(IC_Utility(kKeyedStoreIC_Miss)));
+}
+
+
+static Address IC_utilities[] = {
+#define ADDR(name) FUNCTION_ADDR(name),
+ IC_UTIL_LIST(ADDR)
+ NULL
+#undef ADDR
+};
+
+
+Address IC::AddressFromUtilityId(IC::UtilityId id) {
+ return IC_utilities[id];
+}
+
+
+} } // namespace v8::internal