| // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | #include "vm/compiler/method_recognizer.h" | 
 |  | 
 | #include "vm/log.h" | 
 | #include "vm/object.h" | 
 | #include "vm/reusable_handles.h" | 
 | #include "vm/symbols.h" | 
 |  | 
 | namespace dart { | 
 |  | 
 | intptr_t MethodRecognizer::NumArgsCheckedForStaticCall( | 
 |     const Function& function) { | 
 |   switch (function.recognized_kind()) { | 
 |     case MethodRecognizer::kDoubleFromInteger: | 
 |     case MethodRecognizer::kMathMin: | 
 |     case MethodRecognizer::kMathMax: | 
 |       return 2; | 
 |     default: | 
 |       return 0; | 
 |   } | 
 | } | 
 |  | 
 | intptr_t MethodRecognizer::ResultCidFromPragma( | 
 |     const Object& function_or_field) { | 
 |   auto T = Thread::Current(); | 
 |   auto Z = T->zone(); | 
 |   auto& option = Object::Handle(Z); | 
 |   if (Library::FindPragma(T, /*only_core=*/true, function_or_field, | 
 |                           Symbols::vm_exact_result_type(), | 
 |                           /*multiple=*/false, &option)) { | 
 |     if (option.IsType()) { | 
 |       return Type::Cast(option).type_class_id(); | 
 |     } else if (option.IsString()) { | 
 |       auto& str = String::Cast(option); | 
 |       // 'str' should match the pattern '([^#]+)#([^#\?]+)' where group 1 | 
 |       // is the library URI and group 2 is the class name. | 
 |       bool parse_failure = false; | 
 |       intptr_t library_end = -1; | 
 |       for (intptr_t i = 0; i < str.Length(); ++i) { | 
 |         if (str.CharAt(i) == '#') { | 
 |           if (library_end != -1) { | 
 |             parse_failure = true; | 
 |             break; | 
 |           } else { | 
 |             library_end = i; | 
 |           } | 
 |         } | 
 |       } | 
 |       if (!parse_failure && library_end > 0) { | 
 |         auto& tmp = | 
 |             String::Handle(String::SubString(str, 0, library_end, Heap::kOld)); | 
 |         const auto& library = Library::Handle(Library::LookupLibrary(T, tmp)); | 
 |         if (!library.IsNull()) { | 
 |           tmp = String::SubString(str, library_end + 1, | 
 |                                   str.Length() - library_end - 1, Heap::kOld); | 
 |           const auto& klass = | 
 |               Class::Handle(library.LookupClassAllowPrivate(tmp)); | 
 |           if (!klass.IsNull()) { | 
 |             return klass.id(); | 
 |           } | 
 |         } | 
 |       } | 
 |     } else if (option.IsArray()) { | 
 |       const Array& array = Array::Cast(option); | 
 |       if (array.Length() > 0) { | 
 |         const Object& type = Object::Handle(Array::Cast(option).At(0)); | 
 |         if (type.IsType()) { | 
 |           return Type::Cast(type).type_class_id(); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   return kDynamicCid; | 
 | } | 
 |  | 
 | intptr_t MethodRecognizer::MethodKindToReceiverCid(Kind kind) { | 
 |   switch (kind) { | 
 |     case kObjectArrayGetIndexed: | 
 |     case kObjectArraySetIndexed: | 
 |     case kObjectArraySetIndexedUnchecked: | 
 |       return kArrayCid; | 
 |  | 
 |     case kGrowableArrayGetIndexed: | 
 |     case kGrowableArraySetIndexed: | 
 |     case kGrowableArraySetIndexedUnchecked: | 
 |       return kGrowableObjectArrayCid; | 
 |  | 
 | #define TYPED_DATA_GET_SET_INDEXED_CASES(clazz)                                \ | 
 |   case k##clazz##ArrayGetIndexed:                                              \ | 
 |   case k##clazz##ArraySetIndexed:                                              \ | 
 |     return kTypedData##clazz##ArrayCid;                                        \ | 
 |   case kExternal##clazz##ArrayGetIndexed:                                      \ | 
 |     return kExternalTypedData##clazz##ArrayCid;                                \ | 
 |   case k##clazz##ArrayViewGetIndexed:                                          \ | 
 |     return kTypedData##clazz##ArrayViewCid; | 
 |  | 
 |       DART_CLASS_LIST_TYPED_DATA(TYPED_DATA_GET_SET_INDEXED_CASES); | 
 | #undef TYPED_DATA_GET_SET_INDEXED_CASES | 
 |  | 
 |     case kExternalUint8ArraySetIndexed: | 
 |       return kExternalTypedDataUint8ArrayCid; | 
 |  | 
 |     case kExternalUint8ClampedArraySetIndexed: | 
 |       return kExternalTypedDataUint8ClampedArrayCid; | 
 |  | 
 |     default: | 
 |       break; | 
 |   } | 
 |   UNREACHABLE(); | 
 |   return kIllegalCid; | 
 | } | 
 |  | 
 | static const struct { | 
 |   const char* const function_name; | 
 |   const char* const enum_name; | 
 | } recognized_methods[MethodRecognizer::kNumRecognizedMethods] = { | 
 |     {"", "Unknown"}, | 
 | #define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp)    \ | 
 |   {"" #function_name, #enum_name}, | 
 |     RECOGNIZED_LIST(RECOGNIZE_METHOD) | 
 | #undef RECOGNIZE_METHOD | 
 | }; | 
 |  | 
 | const char* MethodRecognizer::KindToCString(Kind kind) { | 
 |   if (kind >= kUnknown && kind < kNumRecognizedMethods) | 
 |     return recognized_methods[kind].enum_name; | 
 |   return "?"; | 
 | } | 
 |  | 
 | const char* MethodRecognizer::KindToFunctionNameCString(Kind kind) { | 
 |   if (kind >= kUnknown && kind < kNumRecognizedMethods) | 
 |     return recognized_methods[kind].function_name; | 
 |   return "?"; | 
 | } | 
 |  | 
 | // Is this method marked with the vm:recognized pragma? | 
 | bool MethodRecognizer::IsMarkedAsRecognized(const Function& function, | 
 |                                             const char* kind) { | 
 |   const Function* functionp = | 
 |       function.IsDynamicInvocationForwarder() | 
 |           ? &Function::Handle(function.ForwardingTarget()) | 
 |           : &function; | 
 |   Object& options = Object::Handle(); | 
 |   bool is_recognized = Library::FindPragma( | 
 |       Thread::Current(), /*only_core=*/true, *functionp, | 
 |       Symbols::vm_recognized(), /*multiple=*/false, &options); | 
 |   if (!is_recognized) return false; | 
 |   if (kind == nullptr) return true; | 
 |  | 
 |   ASSERT(options.IsString()); | 
 |   ASSERT(String::Cast(options).Equals("asm-intrinsic") || | 
 |          String::Cast(options).Equals("graph-intrinsic") || | 
 |          String::Cast(options).Equals("other")); | 
 |   return String::Cast(options).Equals(kind); | 
 | } | 
 |  | 
 | static bool IsAssemblerIntrinsic(MethodRecognizer::Kind kind) { | 
 |   switch (kind) { | 
 | #define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp)    \ | 
 |   case MethodRecognizer::k##enum_name: | 
 |     ASM_INTRINSICS_LIST(RECOGNIZE_METHOD) | 
 | #undef RECOGNIZE_METHOD | 
 |     return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | static bool IsGraphIntrinsic(MethodRecognizer::Kind kind) { | 
 |   switch (kind) { | 
 | #define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp)    \ | 
 |   case MethodRecognizer::k##enum_name: | 
 |     GRAPH_INTRINSICS_LIST(RECOGNIZE_METHOD) | 
 | #undef RECOGNIZE_METHOD | 
 |     return true; | 
 |     default: | 
 |       return false; | 
 |   } | 
 | } | 
 |  | 
 | void MethodRecognizer::InitializeState() { | 
 |   Library& lib = Library::Handle(); | 
 |   Function& func = Function::Handle(); | 
 |   bool fingerprints_match = true; | 
 |  | 
 | #define RECOGNIZE_METHOD(library, class_name, function_name, enum_name, fp)    \ | 
 |   lib = Library::library();                                                    \ | 
 |   func = Library::GetFunction(lib, #class_name, #function_name);               \ | 
 |   if (!func.IsNull()) {                                                        \ | 
 |     fingerprints_match =                                                       \ | 
 |         func.CheckSourceFingerprint(fp) && fingerprints_match;                 \ | 
 |     func.set_recognized_kind(k##enum_name);                                    \ | 
 |     if (IsAssemblerIntrinsic(k##enum_name)) {                                  \ | 
 |       func.reset_unboxed_parameters_and_return();                              \ | 
 |       func.set_is_intrinsic(true);                                             \ | 
 |     } else if (IsGraphIntrinsic(k##enum_name)) {                               \ | 
 |       func.set_is_intrinsic(true);                                             \ | 
 |     }                                                                          \ | 
 |   } else if (!FLAG_precompiled_mode) {                                         \ | 
 |     fingerprints_match = false;                                                \ | 
 |     OS::PrintErr("Missing %s %s::%s\n", #library, #class_name,                 \ | 
 |                  #function_name);                                              \ | 
 |   } | 
 |   RECOGNIZED_LIST(RECOGNIZE_METHOD) | 
 | #undef RECOGNIZE_METHOD | 
 |  | 
 | #define SET_FUNCTION_BIT(library, class_name, function_name, dest, fp, setter, \ | 
 |                          value)                                                \ | 
 |   lib = Library::library();                                                    \ | 
 |   func = Library::GetFunction(lib, #class_name, #function_name);               \ | 
 |   if (!func.IsNull()) {                                                        \ | 
 |     fingerprints_match =                                                       \ | 
 |         func.CheckSourceFingerprint(fp) && fingerprints_match;                 \ | 
 |     func.setter(value);                                                        \ | 
 |   } else if (!FLAG_precompiled_mode) {                                         \ | 
 |     OS::PrintErr("Missing %s::%s\n", #class_name, #function_name);             \ | 
 |     fingerprints_match = false;                                                \ | 
 |   } | 
 |  | 
 | #define SET_IS_POLYMORPHIC_TARGET(library, class_name, function_name, dest,    \ | 
 |                                   fp)                                          \ | 
 |   SET_FUNCTION_BIT(library, class_name, function_name, dest, fp,               \ | 
 |                    set_is_polymorphic_target, true) | 
 |  | 
 |   POLYMORPHIC_TARGET_LIST(SET_IS_POLYMORPHIC_TARGET); | 
 |  | 
 | #undef SET_RECOGNIZED_KIND | 
 | #undef SET_IS_POLYMORPHIC_TARGET | 
 | #undef SET_FUNCTION_BIT | 
 |  | 
 |   if (!fingerprints_match) { | 
 |     // Private names are mangled. Mangling depends on Library::private_key_. | 
 |     // If registering a new bootstrap library, add at the end. | 
 |     FATAL( | 
 |         "FP mismatch while recognizing methods. If the behavior of " | 
 |         "these functions has changed, then changes are also needed in " | 
 |         "the VM's compiler. Otherwise the fingerprint can simply be " | 
 |         "updated in recognized_methods_list.h\n"); | 
 |   } | 
 | } | 
 |  | 
 | static Token::Kind RecognizeTokenKindHelper(const String& name) { | 
 |   if (name.ptr() == Symbols::Plus().ptr()) { | 
 |     return Token::kADD; | 
 |   } else if (name.ptr() == Symbols::Minus().ptr()) { | 
 |     return Token::kSUB; | 
 |   } else if (name.ptr() == Symbols::Star().ptr()) { | 
 |     return Token::kMUL; | 
 |   } else if (name.ptr() == Symbols::Slash().ptr()) { | 
 |     return Token::kDIV; | 
 |   } else if (name.ptr() == Symbols::TruncDivOperator().ptr()) { | 
 |     return Token::kTRUNCDIV; | 
 |   } else if (name.ptr() == Symbols::Percent().ptr()) { | 
 |     return Token::kMOD; | 
 |   } else if (name.ptr() == Symbols::BitOr().ptr()) { | 
 |     return Token::kBIT_OR; | 
 |   } else if (name.ptr() == Symbols::Ampersand().ptr()) { | 
 |     return Token::kBIT_AND; | 
 |   } else if (name.ptr() == Symbols::Caret().ptr()) { | 
 |     return Token::kBIT_XOR; | 
 |   } else if (name.ptr() == Symbols::LeftShiftOperator().ptr()) { | 
 |     return Token::kSHL; | 
 |   } else if (name.ptr() == Symbols::RightShiftOperator().ptr()) { | 
 |     return Token::kSHR; | 
 |   } else if (name.ptr() == Symbols::UnsignedRightShiftOperator().ptr()) { | 
 |     return Token::kUSHR; | 
 |   } else if (name.ptr() == Symbols::Tilde().ptr()) { | 
 |     return Token::kBIT_NOT; | 
 |   } else if (name.ptr() == Symbols::UnaryMinus().ptr()) { | 
 |     return Token::kNEGATE; | 
 |   } else if (name.ptr() == Symbols::EqualOperator().ptr()) { | 
 |     return Token::kEQ; | 
 |   } else if (name.ptr() == Symbols::Token(Token::kNE).ptr()) { | 
 |     return Token::kNE; | 
 |   } else if (name.ptr() == Symbols::LAngleBracket().ptr()) { | 
 |     return Token::kLT; | 
 |   } else if (name.ptr() == Symbols::RAngleBracket().ptr()) { | 
 |     return Token::kGT; | 
 |   } else if (name.ptr() == Symbols::LessEqualOperator().ptr()) { | 
 |     return Token::kLTE; | 
 |   } else if (name.ptr() == Symbols::GreaterEqualOperator().ptr()) { | 
 |     return Token::kGTE; | 
 |   } else if (Field::IsGetterName(name)) { | 
 |     return Token::kGET; | 
 |   } else if (Field::IsSetterName(name)) { | 
 |     return Token::kSET; | 
 |   } | 
 |   return Token::kILLEGAL; | 
 | } | 
 |  | 
 | Token::Kind MethodTokenRecognizer::RecognizeTokenKind(const String& name) { | 
 |   ASSERT(name.IsSymbol()); | 
 |   if (Function::IsDynamicInvocationForwarderName(name)) { | 
 |     Thread* thread = Thread::Current(); | 
 |     const auto& demangled_name = String::Handle( | 
 |         thread->zone(), Function::DemangleDynamicInvocationForwarderName(name)); | 
 |     return RecognizeTokenKindHelper(demangled_name); | 
 |   } else { | 
 |     return RecognizeTokenKindHelper(name); | 
 |   } | 
 | } | 
 |  | 
 | #define RECOGNIZE_FACTORY(symbol, library, class_name, constructor_name, cid,  \ | 
 |                           fp)                                                  \ | 
 |   {Symbols::k##symbol##Id, cid, fp, #symbol ", " #cid},  // NOLINT | 
 |  | 
 | static const struct { | 
 |   const intptr_t symbol_id; | 
 |   const intptr_t cid; | 
 |   const uint32_t finger_print; | 
 |   const char* const name; | 
 | } factory_recognizer_list[] = {RECOGNIZED_LIST_FACTORY_LIST(RECOGNIZE_FACTORY){ | 
 |     Symbols::kIllegal, -1, 0, nullptr}}; | 
 |  | 
 | #undef RECOGNIZE_FACTORY | 
 |  | 
 | intptr_t FactoryRecognizer::ResultCid(const Function& factory) { | 
 |   ASSERT(factory.IsFactory()); | 
 |   const Class& function_class = Class::Handle(factory.Owner()); | 
 |   const Library& lib = Library::Handle(function_class.library()); | 
 |   ASSERT((lib.ptr() == Library::CoreLibrary()) || | 
 |          (lib.ptr() == Library::TypedDataLibrary())); | 
 |   const String& factory_name = String::Handle(factory.name()); | 
 |   for (intptr_t i = 0; | 
 |        factory_recognizer_list[i].symbol_id != Symbols::kIllegal; i++) { | 
 |     if (String::EqualsIgnoringPrivateKey( | 
 |             factory_name, | 
 |             Symbols::Symbol(factory_recognizer_list[i].symbol_id))) { | 
 |       return factory_recognizer_list[i].cid; | 
 |     } | 
 |   } | 
 |   return kDynamicCid; | 
 | } | 
 |  | 
 | intptr_t FactoryRecognizer::GetResultCidOfListFactory(Zone* zone, | 
 |                                                       const Function& function, | 
 |                                                       intptr_t argument_count) { | 
 |   if (!function.IsFactory()) { | 
 |     return kDynamicCid; | 
 |   } | 
 |  | 
 |   const Class& owner = Class::Handle(zone, function.Owner()); | 
 |   if ((owner.library() != Library::CoreLibrary()) && | 
 |       (owner.library() != Library::TypedDataLibrary())) { | 
 |     return kDynamicCid; | 
 |   } | 
 |  | 
 |   if (owner.Name() == Symbols::List().ptr()) { | 
 |     if (function.name() == Symbols::ListFactory().ptr()) { | 
 |       ASSERT(argument_count == 1 || argument_count == 2); | 
 |       return (argument_count == 1) ? kGrowableObjectArrayCid : kArrayCid; | 
 |     } else if (function.name() == Symbols::ListFilledFactory().ptr()) { | 
 |       ASSERT(argument_count == 3 || argument_count == 4); | 
 |       return (argument_count == 3) ? kArrayCid : kDynamicCid; | 
 |     } | 
 |   } | 
 |  | 
 |   return ResultCid(function); | 
 | } | 
 |  | 
 | }  // namespace dart |