| // Copyright 2012 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 "ast.h" | 
 | #include "code-stubs.h" | 
 | #include "compiler.h" | 
 | #include "ic.h" | 
 | #include "macro-assembler.h" | 
 | #include "stub-cache.h" | 
 | #include "type-info.h" | 
 |  | 
 | #include "ic-inl.h" | 
 | #include "objects-inl.h" | 
 |  | 
 | namespace v8 { | 
 | namespace internal { | 
 |  | 
 |  | 
 | TypeInfo TypeInfo::TypeFromValue(Handle<Object> value) { | 
 |   TypeInfo info; | 
 |   if (value->IsSmi()) { | 
 |     info = TypeInfo::Smi(); | 
 |   } else if (value->IsHeapNumber()) { | 
 |     info = TypeInfo::IsInt32Double(HeapNumber::cast(*value)->value()) | 
 |         ? TypeInfo::Integer32() | 
 |         : TypeInfo::Double(); | 
 |   } else if (value->IsString()) { | 
 |     info = TypeInfo::String(); | 
 |   } else { | 
 |     info = TypeInfo::Unknown(); | 
 |   } | 
 |   return info; | 
 | } | 
 |  | 
 |  | 
 | TypeFeedbackOracle::TypeFeedbackOracle(Handle<Code> code, | 
 |                                        Handle<Context> global_context, | 
 |                                        Isolate* isolate, | 
 |                                        Zone* zone) { | 
 |   global_context_ = global_context; | 
 |   isolate_ = isolate; | 
 |   zone_ = zone; | 
 |   BuildDictionary(code); | 
 |   ASSERT(reinterpret_cast<Address>(*dictionary_.location()) != kHandleZapValue); | 
 | } | 
 |  | 
 |  | 
 | static uint32_t IdToKey(TypeFeedbackId ast_id) { | 
 |   return static_cast<uint32_t>(ast_id.ToInt()); | 
 | } | 
 |  | 
 |  | 
 | Handle<Object> TypeFeedbackOracle::GetInfo(TypeFeedbackId ast_id) { | 
 |   int entry = dictionary_->FindEntry(IdToKey(ast_id)); | 
 |   return entry != UnseededNumberDictionary::kNotFound | 
 |       ? Handle<Object>(dictionary_->ValueAt(entry)) | 
 |       : Handle<Object>::cast(isolate_->factory()->undefined_value()); | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::LoadIsUninitialized(Property* expr) { | 
 |   Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId()); | 
 |   if (map_or_code->IsMap()) return false; | 
 |   if (map_or_code->IsCode()) { | 
 |     Handle<Code> code = Handle<Code>::cast(map_or_code); | 
 |     return code->is_inline_cache_stub() && code->ic_state() == UNINITIALIZED; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::LoadIsMonomorphicNormal(Property* expr) { | 
 |   Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId()); | 
 |   if (map_or_code->IsMap()) return true; | 
 |   if (map_or_code->IsCode()) { | 
 |     Handle<Code> code = Handle<Code>::cast(map_or_code); | 
 |     bool preliminary_checks = code->is_keyed_load_stub() && | 
 |         code->ic_state() == MONOMORPHIC && | 
 |         Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL; | 
 |     if (!preliminary_checks) return false; | 
 |     Map* map = code->FindFirstMap(); | 
 |     return map != NULL && !CanRetainOtherContext(map, *global_context_); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::LoadIsMegamorphicWithTypeInfo(Property* expr) { | 
 |   Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId()); | 
 |   if (map_or_code->IsCode()) { | 
 |     Handle<Code> code = Handle<Code>::cast(map_or_code); | 
 |     Builtins* builtins = isolate_->builtins(); | 
 |     return code->is_keyed_load_stub() && | 
 |         *code != builtins->builtin(Builtins::kKeyedLoadIC_Generic) && | 
 |         code->ic_state() == MEGAMORPHIC; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::StoreIsMonomorphicNormal(TypeFeedbackId ast_id) { | 
 |   Handle<Object> map_or_code = GetInfo(ast_id); | 
 |   if (map_or_code->IsMap()) return true; | 
 |   if (map_or_code->IsCode()) { | 
 |     Handle<Code> code = Handle<Code>::cast(map_or_code); | 
 |     bool allow_growth = | 
 |         Code::GetKeyedAccessGrowMode(code->extra_ic_state()) == | 
 |         ALLOW_JSARRAY_GROWTH; | 
 |     bool preliminary_checks = | 
 |         code->is_keyed_store_stub() && | 
 |         !allow_growth && | 
 |         code->ic_state() == MONOMORPHIC && | 
 |         Code::ExtractTypeFromFlags(code->flags()) == Code::NORMAL; | 
 |     if (!preliminary_checks) return false; | 
 |     Map* map = code->FindFirstMap(); | 
 |     return map != NULL && !CanRetainOtherContext(map, *global_context_); | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::StoreIsMegamorphicWithTypeInfo(TypeFeedbackId ast_id) { | 
 |   Handle<Object> map_or_code = GetInfo(ast_id); | 
 |   if (map_or_code->IsCode()) { | 
 |     Handle<Code> code = Handle<Code>::cast(map_or_code); | 
 |     Builtins* builtins = isolate_->builtins(); | 
 |     bool allow_growth = | 
 |         Code::GetKeyedAccessGrowMode(code->extra_ic_state()) == | 
 |         ALLOW_JSARRAY_GROWTH; | 
 |     return code->is_keyed_store_stub() && | 
 |         !allow_growth && | 
 |         *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic) && | 
 |         *code != builtins->builtin(Builtins::kKeyedStoreIC_Generic_Strict) && | 
 |         code->ic_state() == MEGAMORPHIC; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::CallIsMonomorphic(Call* expr) { | 
 |   Handle<Object> value = GetInfo(expr->CallFeedbackId()); | 
 |   return value->IsMap() || value->IsSmi() || value->IsJSFunction(); | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::CallNewIsMonomorphic(CallNew* expr) { | 
 |   Handle<Object> value = GetInfo(expr->CallNewFeedbackId()); | 
 |   return value->IsJSFunction(); | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::ObjectLiteralStoreIsMonomorphic( | 
 |     ObjectLiteral::Property* prop) { | 
 |   Handle<Object> map_or_code = GetInfo(prop->key()->LiteralFeedbackId()); | 
 |   return map_or_code->IsMap(); | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::IsForInFastCase(ForInStatement* stmt) { | 
 |   Handle<Object> value = GetInfo(stmt->ForInFeedbackId()); | 
 |   return value->IsSmi() && | 
 |       Smi::cast(*value)->value() == TypeFeedbackCells::kForInFastCaseMarker; | 
 | } | 
 |  | 
 |  | 
 | Handle<Map> TypeFeedbackOracle::LoadMonomorphicReceiverType(Property* expr) { | 
 |   ASSERT(LoadIsMonomorphicNormal(expr)); | 
 |   Handle<Object> map_or_code = GetInfo(expr->PropertyFeedbackId()); | 
 |   if (map_or_code->IsCode()) { | 
 |     Handle<Code> code = Handle<Code>::cast(map_or_code); | 
 |     Map* first_map = code->FindFirstMap(); | 
 |     ASSERT(first_map != NULL); | 
 |     return CanRetainOtherContext(first_map, *global_context_) | 
 |         ? Handle<Map>::null() | 
 |         : Handle<Map>(first_map); | 
 |   } | 
 |   return Handle<Map>::cast(map_or_code); | 
 | } | 
 |  | 
 |  | 
 | Handle<Map> TypeFeedbackOracle::StoreMonomorphicReceiverType( | 
 |     TypeFeedbackId ast_id) { | 
 |   ASSERT(StoreIsMonomorphicNormal(ast_id)); | 
 |   Handle<Object> map_or_code = GetInfo(ast_id); | 
 |   if (map_or_code->IsCode()) { | 
 |     Handle<Code> code = Handle<Code>::cast(map_or_code); | 
 |     Map* first_map = code->FindFirstMap(); | 
 |     ASSERT(first_map != NULL); | 
 |     return CanRetainOtherContext(first_map, *global_context_) | 
 |         ? Handle<Map>::null() | 
 |         : Handle<Map>(first_map); | 
 |   } | 
 |   return Handle<Map>::cast(map_or_code); | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::LoadReceiverTypes(Property* expr, | 
 |                                            Handle<String> name, | 
 |                                            SmallMapList* types) { | 
 |   Code::Flags flags = | 
 |       Code::ComputeMonomorphicFlags(Code::LOAD_IC, Code::NORMAL); | 
 |   CollectReceiverTypes(expr->PropertyFeedbackId(), name, flags, types); | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::StoreReceiverTypes(Assignment* expr, | 
 |                                             Handle<String> name, | 
 |                                             SmallMapList* types) { | 
 |   Code::Flags flags = | 
 |       Code::ComputeMonomorphicFlags(Code::STORE_IC, Code::NORMAL); | 
 |   CollectReceiverTypes(expr->AssignmentFeedbackId(), name, flags, types); | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::CallReceiverTypes(Call* expr, | 
 |                                            Handle<String> name, | 
 |                                            CallKind call_kind, | 
 |                                            SmallMapList* types) { | 
 |   int arity = expr->arguments()->length(); | 
 |  | 
 |   // Note: Currently we do not take string extra ic data into account | 
 |   // here. | 
 |   Code::ExtraICState extra_ic_state = | 
 |       CallIC::Contextual::encode(call_kind == CALL_AS_FUNCTION); | 
 |  | 
 |   Code::Flags flags = Code::ComputeMonomorphicFlags(Code::CALL_IC, | 
 |                                                     Code::NORMAL, | 
 |                                                     extra_ic_state, | 
 |                                                     OWN_MAP, | 
 |                                                     arity); | 
 |   CollectReceiverTypes(expr->CallFeedbackId(), name, flags, types); | 
 | } | 
 |  | 
 |  | 
 | CheckType TypeFeedbackOracle::GetCallCheckType(Call* expr) { | 
 |   Handle<Object> value = GetInfo(expr->CallFeedbackId()); | 
 |   if (!value->IsSmi()) return RECEIVER_MAP_CHECK; | 
 |   CheckType check = static_cast<CheckType>(Smi::cast(*value)->value()); | 
 |   ASSERT(check != RECEIVER_MAP_CHECK); | 
 |   return check; | 
 | } | 
 |  | 
 |  | 
 | Handle<JSObject> TypeFeedbackOracle::GetPrototypeForPrimitiveCheck( | 
 |     CheckType check) { | 
 |   JSFunction* function = NULL; | 
 |   switch (check) { | 
 |     case RECEIVER_MAP_CHECK: | 
 |       UNREACHABLE(); | 
 |       break; | 
 |     case STRING_CHECK: | 
 |       function = global_context_->string_function(); | 
 |       break; | 
 |     case NUMBER_CHECK: | 
 |       function = global_context_->number_function(); | 
 |       break; | 
 |     case BOOLEAN_CHECK: | 
 |       function = global_context_->boolean_function(); | 
 |       break; | 
 |   } | 
 |   ASSERT(function != NULL); | 
 |   return Handle<JSObject>(JSObject::cast(function->instance_prototype())); | 
 | } | 
 |  | 
 |  | 
 | Handle<JSFunction> TypeFeedbackOracle::GetCallTarget(Call* expr) { | 
 |   return Handle<JSFunction>::cast(GetInfo(expr->CallFeedbackId())); | 
 | } | 
 |  | 
 |  | 
 | Handle<JSFunction> TypeFeedbackOracle::GetCallNewTarget(CallNew* expr) { | 
 |   return Handle<JSFunction>::cast(GetInfo(expr->CallNewFeedbackId())); | 
 | } | 
 |  | 
 |  | 
 | Handle<Map> TypeFeedbackOracle::GetObjectLiteralStoreMap( | 
 |     ObjectLiteral::Property* prop) { | 
 |   ASSERT(ObjectLiteralStoreIsMonomorphic(prop)); | 
 |   return Handle<Map>::cast(GetInfo(prop->key()->LiteralFeedbackId())); | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::LoadIsBuiltin(Property* expr, Builtins::Name id) { | 
 |   return *GetInfo(expr->PropertyFeedbackId()) == | 
 |       isolate_->builtins()->builtin(id); | 
 | } | 
 |  | 
 |  | 
 | TypeInfo TypeFeedbackOracle::CompareType(CompareOperation* expr) { | 
 |   Handle<Object> object = GetInfo(expr->CompareOperationFeedbackId()); | 
 |   TypeInfo unknown = TypeInfo::Unknown(); | 
 |   if (!object->IsCode()) return unknown; | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   if (!code->is_compare_ic_stub()) return unknown; | 
 |  | 
 |   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); | 
 |   switch (state) { | 
 |     case CompareIC::UNINITIALIZED: | 
 |       // Uninitialized means never executed. | 
 |       return TypeInfo::Uninitialized(); | 
 |     case CompareIC::SMIS: | 
 |       return TypeInfo::Smi(); | 
 |     case CompareIC::HEAP_NUMBERS: | 
 |       return TypeInfo::Number(); | 
 |     case CompareIC::SYMBOLS: | 
 |     case CompareIC::STRINGS: | 
 |       return TypeInfo::String(); | 
 |     case CompareIC::OBJECTS: | 
 |     case CompareIC::KNOWN_OBJECTS: | 
 |       // TODO(kasperl): We really need a type for JS objects here. | 
 |       return TypeInfo::NonPrimitive(); | 
 |     case CompareIC::GENERIC: | 
 |     default: | 
 |       return unknown; | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::IsSymbolCompare(CompareOperation* expr) { | 
 |   Handle<Object> object = GetInfo(expr->CompareOperationFeedbackId()); | 
 |   if (!object->IsCode()) return false; | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   if (!code->is_compare_ic_stub()) return false; | 
 |   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); | 
 |   return state == CompareIC::SYMBOLS; | 
 | } | 
 |  | 
 |  | 
 | Handle<Map> TypeFeedbackOracle::GetCompareMap(CompareOperation* expr) { | 
 |   Handle<Object> object = GetInfo(expr->CompareOperationFeedbackId()); | 
 |   if (!object->IsCode()) return Handle<Map>::null(); | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   if (!code->is_compare_ic_stub()) return Handle<Map>::null(); | 
 |   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); | 
 |   if (state != CompareIC::KNOWN_OBJECTS) { | 
 |     return Handle<Map>::null(); | 
 |   } | 
 |   Map* first_map = code->FindFirstMap(); | 
 |   ASSERT(first_map != NULL); | 
 |   return CanRetainOtherContext(first_map, *global_context_) | 
 |       ? Handle<Map>::null() | 
 |       : Handle<Map>(first_map); | 
 | } | 
 |  | 
 |  | 
 | TypeInfo TypeFeedbackOracle::UnaryType(UnaryOperation* expr) { | 
 |   Handle<Object> object = GetInfo(expr->UnaryOperationFeedbackId()); | 
 |   TypeInfo unknown = TypeInfo::Unknown(); | 
 |   if (!object->IsCode()) return unknown; | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   ASSERT(code->is_unary_op_stub()); | 
 |   UnaryOpIC::TypeInfo type = static_cast<UnaryOpIC::TypeInfo>( | 
 |       code->unary_op_type()); | 
 |   switch (type) { | 
 |     case UnaryOpIC::SMI: | 
 |       return TypeInfo::Smi(); | 
 |     case UnaryOpIC::HEAP_NUMBER: | 
 |       return TypeInfo::Double(); | 
 |     default: | 
 |       return unknown; | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | TypeInfo TypeFeedbackOracle::BinaryType(BinaryOperation* expr) { | 
 |   Handle<Object> object = GetInfo(expr->BinaryOperationFeedbackId()); | 
 |   TypeInfo unknown = TypeInfo::Unknown(); | 
 |   if (!object->IsCode()) return unknown; | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   if (code->is_binary_op_stub()) { | 
 |     BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>( | 
 |         code->binary_op_type()); | 
 |     BinaryOpIC::TypeInfo result_type = static_cast<BinaryOpIC::TypeInfo>( | 
 |         code->binary_op_result_type()); | 
 |  | 
 |     switch (type) { | 
 |       case BinaryOpIC::UNINITIALIZED: | 
 |         // Uninitialized means never executed. | 
 |         return TypeInfo::Uninitialized(); | 
 |       case BinaryOpIC::SMI: | 
 |         switch (result_type) { | 
 |           case BinaryOpIC::UNINITIALIZED: | 
 |             if (expr->op() == Token::DIV) { | 
 |               return TypeInfo::Double(); | 
 |             } | 
 |             return TypeInfo::Smi(); | 
 |           case BinaryOpIC::SMI: | 
 |             return TypeInfo::Smi(); | 
 |           case BinaryOpIC::INT32: | 
 |             return TypeInfo::Integer32(); | 
 |           case BinaryOpIC::HEAP_NUMBER: | 
 |             return TypeInfo::Double(); | 
 |           default: | 
 |             return unknown; | 
 |         } | 
 |       case BinaryOpIC::INT32: | 
 |         if (expr->op() == Token::DIV || | 
 |             result_type == BinaryOpIC::HEAP_NUMBER) { | 
 |           return TypeInfo::Double(); | 
 |         } | 
 |         return TypeInfo::Integer32(); | 
 |       case BinaryOpIC::HEAP_NUMBER: | 
 |         return TypeInfo::Double(); | 
 |       case BinaryOpIC::BOTH_STRING: | 
 |         return TypeInfo::String(); | 
 |       case BinaryOpIC::STRING: | 
 |       case BinaryOpIC::GENERIC: | 
 |         return unknown; | 
 |      default: | 
 |         return unknown; | 
 |     } | 
 |   } | 
 |   return unknown; | 
 | } | 
 |  | 
 |  | 
 | TypeInfo TypeFeedbackOracle::SwitchType(CaseClause* clause) { | 
 |   Handle<Object> object = GetInfo(clause->CompareId()); | 
 |   TypeInfo unknown = TypeInfo::Unknown(); | 
 |   if (!object->IsCode()) return unknown; | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   if (!code->is_compare_ic_stub()) return unknown; | 
 |  | 
 |   CompareIC::State state = static_cast<CompareIC::State>(code->compare_state()); | 
 |   switch (state) { | 
 |     case CompareIC::UNINITIALIZED: | 
 |       // Uninitialized means never executed. | 
 |       // TODO(fschneider): Introduce a separate value for never-executed ICs. | 
 |       return unknown; | 
 |     case CompareIC::SMIS: | 
 |       return TypeInfo::Smi(); | 
 |     case CompareIC::STRINGS: | 
 |       return TypeInfo::String(); | 
 |     case CompareIC::SYMBOLS: | 
 |       return TypeInfo::Symbol(); | 
 |     case CompareIC::HEAP_NUMBERS: | 
 |       return TypeInfo::Number(); | 
 |     case CompareIC::OBJECTS: | 
 |     case CompareIC::KNOWN_OBJECTS: | 
 |       // TODO(kasperl): We really need a type for JS objects here. | 
 |       return TypeInfo::NonPrimitive(); | 
 |     case CompareIC::GENERIC: | 
 |     default: | 
 |       return unknown; | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | TypeInfo TypeFeedbackOracle::IncrementType(CountOperation* expr) { | 
 |   Handle<Object> object = GetInfo(expr->CountBinOpFeedbackId()); | 
 |   TypeInfo unknown = TypeInfo::Unknown(); | 
 |   if (!object->IsCode()) return unknown; | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   if (!code->is_binary_op_stub()) return unknown; | 
 |  | 
 |   BinaryOpIC::TypeInfo type = static_cast<BinaryOpIC::TypeInfo>( | 
 |       code->binary_op_type()); | 
 |   switch (type) { | 
 |     case BinaryOpIC::UNINITIALIZED: | 
 |     case BinaryOpIC::SMI: | 
 |       return TypeInfo::Smi(); | 
 |     case BinaryOpIC::INT32: | 
 |       return TypeInfo::Integer32(); | 
 |     case BinaryOpIC::HEAP_NUMBER: | 
 |       return TypeInfo::Double(); | 
 |     case BinaryOpIC::BOTH_STRING: | 
 |     case BinaryOpIC::STRING: | 
 |     case BinaryOpIC::GENERIC: | 
 |       return unknown; | 
 |     default: | 
 |       return unknown; | 
 |   } | 
 |   UNREACHABLE(); | 
 |   return unknown; | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::CollectReceiverTypes(TypeFeedbackId ast_id, | 
 |                                               Handle<String> name, | 
 |                                               Code::Flags flags, | 
 |                                               SmallMapList* types) { | 
 |   Handle<Object> object = GetInfo(ast_id); | 
 |   if (object->IsUndefined() || object->IsSmi()) return; | 
 |  | 
 |   if (*object == | 
 |       isolate_->builtins()->builtin(Builtins::kStoreIC_GlobalProxy)) { | 
 |     // TODO(fschneider): We could collect the maps and signal that | 
 |     // we need a generic store (or load) here. | 
 |     ASSERT(Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC); | 
 |   } else if (object->IsMap()) { | 
 |     types->Add(Handle<Map>::cast(object), zone()); | 
 |   } else if (FLAG_collect_megamorphic_maps_from_stub_cache && | 
 |       Handle<Code>::cast(object)->ic_state() == MEGAMORPHIC) { | 
 |     types->Reserve(4, zone()); | 
 |     ASSERT(object->IsCode()); | 
 |     isolate_->stub_cache()->CollectMatchingMaps(types, | 
 |                                                 *name, | 
 |                                                 flags, | 
 |                                                 global_context_, | 
 |                                                 zone()); | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | // Check if a map originates from a given global context. We use this | 
 | // information to filter out maps from different context to avoid | 
 | // retaining objects from different tabs in Chrome via optimized code. | 
 | bool TypeFeedbackOracle::CanRetainOtherContext(Map* map, | 
 |                                                Context* global_context) { | 
 |   Object* constructor = NULL; | 
 |   while (!map->prototype()->IsNull()) { | 
 |     constructor = map->constructor(); | 
 |     if (!constructor->IsNull()) { | 
 |       // If the constructor is not null or a JSFunction, we have to | 
 |       // conservatively assume that it may retain a global context. | 
 |       if (!constructor->IsJSFunction()) return true; | 
 |       // Check if the constructor directly references a foreign context. | 
 |       if (CanRetainOtherContext(JSFunction::cast(constructor), | 
 |                                 global_context)) { | 
 |         return true; | 
 |       } | 
 |     } | 
 |     map = HeapObject::cast(map->prototype())->map(); | 
 |   } | 
 |   constructor = map->constructor(); | 
 |   if (constructor->IsNull()) return false; | 
 |   JSFunction* function = JSFunction::cast(constructor); | 
 |   return CanRetainOtherContext(function, global_context); | 
 | } | 
 |  | 
 |  | 
 | bool TypeFeedbackOracle::CanRetainOtherContext(JSFunction* function, | 
 |                                                Context* global_context) { | 
 |   return function->context()->global() != global_context->global() | 
 |       && function->context()->global() != global_context->builtins(); | 
 | } | 
 |  | 
 |  | 
 | static void AddMapIfMissing(Handle<Map> map, SmallMapList* list, | 
 |                             Zone* zone) { | 
 |   for (int i = 0; i < list->length(); ++i) { | 
 |     if (list->at(i).is_identical_to(map)) return; | 
 |   } | 
 |   list->Add(map, zone); | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::CollectKeyedReceiverTypes(TypeFeedbackId ast_id, | 
 |                                                    SmallMapList* types) { | 
 |   Handle<Object> object = GetInfo(ast_id); | 
 |   if (!object->IsCode()) return; | 
 |   Handle<Code> code = Handle<Code>::cast(object); | 
 |   if (code->kind() == Code::KEYED_LOAD_IC || | 
 |       code->kind() == Code::KEYED_STORE_IC) { | 
 |     AssertNoAllocation no_allocation; | 
 |     int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT); | 
 |     for (RelocIterator it(*code, mask); !it.done(); it.next()) { | 
 |       RelocInfo* info = it.rinfo(); | 
 |       Object* object = info->target_object(); | 
 |       if (object->IsMap()) { | 
 |         Map* map = Map::cast(object); | 
 |         if (!CanRetainOtherContext(map, *global_context_)) { | 
 |           AddMapIfMissing(Handle<Map>(map), types, zone()); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | byte TypeFeedbackOracle::ToBooleanTypes(TypeFeedbackId ast_id) { | 
 |   Handle<Object> object = GetInfo(ast_id); | 
 |   return object->IsCode() ? Handle<Code>::cast(object)->to_boolean_state() : 0; | 
 | } | 
 |  | 
 |  | 
 | // Things are a bit tricky here: The iterator for the RelocInfos and the infos | 
 | // themselves are not GC-safe, so we first get all infos, then we create the | 
 | // dictionary (possibly triggering GC), and finally we relocate the collected | 
 | // infos before we process them. | 
 | void TypeFeedbackOracle::BuildDictionary(Handle<Code> code) { | 
 |   AssertNoAllocation no_allocation; | 
 |   ZoneList<RelocInfo> infos(16, zone()); | 
 |   HandleScope scope; | 
 |   GetRelocInfos(code, &infos); | 
 |   CreateDictionary(code, &infos); | 
 |   ProcessRelocInfos(&infos); | 
 |   ProcessTypeFeedbackCells(code); | 
 |   // Allocate handle in the parent scope. | 
 |   dictionary_ = scope.CloseAndEscape(dictionary_); | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::GetRelocInfos(Handle<Code> code, | 
 |                                        ZoneList<RelocInfo>* infos) { | 
 |   int mask = RelocInfo::ModeMask(RelocInfo::CODE_TARGET_WITH_ID); | 
 |   for (RelocIterator it(*code, mask); !it.done(); it.next()) { | 
 |     infos->Add(*it.rinfo(), zone()); | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::CreateDictionary(Handle<Code> code, | 
 |                                           ZoneList<RelocInfo>* infos) { | 
 |   DisableAssertNoAllocation allocation_allowed; | 
 |   int cell_count = code->type_feedback_info()->IsTypeFeedbackInfo() | 
 |       ? TypeFeedbackInfo::cast(code->type_feedback_info())-> | 
 |           type_feedback_cells()->CellCount() | 
 |       : 0; | 
 |   int length = infos->length() + cell_count; | 
 |   byte* old_start = code->instruction_start(); | 
 |   dictionary_ = FACTORY->NewUnseededNumberDictionary(length); | 
 |   byte* new_start = code->instruction_start(); | 
 |   RelocateRelocInfos(infos, old_start, new_start); | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::RelocateRelocInfos(ZoneList<RelocInfo>* infos, | 
 |                                             byte* old_start, | 
 |                                             byte* new_start) { | 
 |   for (int i = 0; i < infos->length(); i++) { | 
 |     RelocInfo* info = &(*infos)[i]; | 
 |     info->set_pc(new_start + (info->pc() - old_start)); | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::ProcessRelocInfos(ZoneList<RelocInfo>* infos) { | 
 |   for (int i = 0; i < infos->length(); i++) { | 
 |     RelocInfo reloc_entry = (*infos)[i]; | 
 |     Address target_address = reloc_entry.target_address(); | 
 |     TypeFeedbackId ast_id = | 
 |         TypeFeedbackId(static_cast<unsigned>((*infos)[i].data())); | 
 |     Code* target = Code::GetCodeFromTargetAddress(target_address); | 
 |     switch (target->kind()) { | 
 |       case Code::LOAD_IC: | 
 |       case Code::STORE_IC: | 
 |       case Code::CALL_IC: | 
 |       case Code::KEYED_CALL_IC: | 
 |         if (target->ic_state() == MONOMORPHIC) { | 
 |           if (target->kind() == Code::CALL_IC && | 
 |               target->check_type() != RECEIVER_MAP_CHECK) { | 
 |             SetInfo(ast_id, Smi::FromInt(target->check_type())); | 
 |           } else { | 
 |             Object* map = target->FindFirstMap(); | 
 |             if (map == NULL) { | 
 |               SetInfo(ast_id, static_cast<Object*>(target)); | 
 |             } else if (!CanRetainOtherContext(Map::cast(map), | 
 |                                               *global_context_)) { | 
 |               SetInfo(ast_id, map); | 
 |             } | 
 |           } | 
 |         } else { | 
 |           SetInfo(ast_id, target); | 
 |         } | 
 |         break; | 
 |  | 
 |       case Code::KEYED_LOAD_IC: | 
 |       case Code::KEYED_STORE_IC: | 
 |         if (target->ic_state() == MONOMORPHIC || | 
 |             target->ic_state() == MEGAMORPHIC) { | 
 |           SetInfo(ast_id, target); | 
 |         } | 
 |         break; | 
 |  | 
 |       case Code::UNARY_OP_IC: | 
 |       case Code::BINARY_OP_IC: | 
 |       case Code::COMPARE_IC: | 
 |       case Code::TO_BOOLEAN_IC: | 
 |         SetInfo(ast_id, target); | 
 |         break; | 
 |  | 
 |       default: | 
 |         break; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::ProcessTypeFeedbackCells(Handle<Code> code) { | 
 |   Object* raw_info = code->type_feedback_info(); | 
 |   if (!raw_info->IsTypeFeedbackInfo()) return; | 
 |   Handle<TypeFeedbackCells> cache( | 
 |       TypeFeedbackInfo::cast(raw_info)->type_feedback_cells()); | 
 |   for (int i = 0; i < cache->CellCount(); i++) { | 
 |     TypeFeedbackId ast_id = cache->AstId(i); | 
 |     Object* value = cache->Cell(i)->value(); | 
 |     if (value->IsSmi() || | 
 |         (value->IsJSFunction() && | 
 |          !CanRetainOtherContext(JSFunction::cast(value), | 
 |                                 *global_context_))) { | 
 |       SetInfo(ast_id, value); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | void TypeFeedbackOracle::SetInfo(TypeFeedbackId ast_id, Object* target) { | 
 |   ASSERT(dictionary_->FindEntry(IdToKey(ast_id)) == | 
 |          UnseededNumberDictionary::kNotFound); | 
 |   MaybeObject* maybe_result = dictionary_->AtNumberPut(IdToKey(ast_id), target); | 
 |   USE(maybe_result); | 
 | #ifdef DEBUG | 
 |   Object* result = NULL; | 
 |   // Dictionary has been allocated with sufficient size for all elements. | 
 |   ASSERT(maybe_result->ToObject(&result)); | 
 |   ASSERT(*dictionary_ == result); | 
 | #endif | 
 | } | 
 |  | 
 | } }  // namespace v8::internal |