| 1 | // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file | 
| 2 | // for details. All rights reserved. Use of this source code is governed by a | 
| 3 | // BSD-style license that can be found in the LICENSE file. | 
| 4 |  | 
| 5 | #include "vm/compiler/method_recognizer.h" | 
| 6 |  | 
| 7 | #include "vm/log.h" | 
| 8 | #include "vm/object.h" | 
| 9 | #include "vm/reusable_handles.h" | 
| 10 | #include "vm/symbols.h" | 
| 11 |  | 
| 12 | namespace dart { | 
| 13 |  | 
| 14 | intptr_t MethodRecognizer::NumArgsCheckedForStaticCall( | 
| 15 |     const Function& function) { | 
| 16 |   switch (function.recognized_kind()) { | 
| 17 |     case MethodRecognizer::kDoubleFromInteger: | 
| 18 |     case MethodRecognizer::kMathMin: | 
| 19 |     case MethodRecognizer::kMathMax: | 
| 20 |       return 2; | 
| 21 |     default: | 
| 22 |       return 0; | 
| 23 |   } | 
| 24 | } | 
| 25 |  | 
| 26 | intptr_t MethodRecognizer::ResultCidFromPragma( | 
| 27 |     const Object& function_or_field) { | 
| 28 |   auto T = Thread::Current(); | 
| 29 |   auto Z = T->zone(); | 
| 30 |   auto& option = Object::Handle(Z); | 
| 31 |   if (Library::FindPragma(T, /*only_core=*/true, function_or_field, | 
| 32 |                           Symbols::vm_exact_result_type(), &option)) { | 
| 33 |     if (option.IsType()) { | 
| 34 |       return Type::Cast(option).type_class_id(); | 
| 35 |     } else if (option.IsString()) { | 
| 36 |       auto& str = String::Cast(option); | 
| 37 |       // 'str' should match the pattern '([^#]+)#([^#\?]+)' where group 1 | 
| 38 |       // is the library URI and group 2 is the class name. | 
| 39 |       bool parse_failure = false; | 
| 40 |       intptr_t library_end = -1; | 
| 41 |       for (intptr_t i = 0; i < str.Length(); ++i) { | 
| 42 |         if (str.CharAt(i) == '#') { | 
| 43 |           if (library_end != -1) { | 
| 44 |             parse_failure = true; | 
| 45 |             break; | 
| 46 |           } else { | 
| 47 |             library_end = i; | 
| 48 |           } | 
| 49 |         } | 
| 50 |       } | 
| 51 |       if (!parse_failure && library_end > 0) { | 
| 52 |         auto& tmp = | 
| 53 |             String::Handle(String::SubString(str, 0, library_end, Heap::kOld)); | 
| 54 |         const auto& library = Library::Handle(Library::LookupLibrary(T, tmp)); | 
| 55 |         if (!library.IsNull()) { | 
| 56 |           tmp = String::SubString(str, library_end + 1, | 
| 57 |                                   str.Length() - library_end - 1, Heap::kOld); | 
| 58 |           const auto& klass = | 
| 59 |               Class::Handle(library.LookupClassAllowPrivate(tmp)); | 
| 60 |           if (!klass.IsNull()) { | 
| 61 |             return klass.id(); | 
| 62 |           } | 
| 63 |         } | 
| 64 |       } | 
| 65 |     } else if (option.IsArray()) { | 
| 66 |       const Array& array = Array::Cast(option); | 
| 67 |       if (array.Length() > 0) { | 
| 68 |         const Object& type = Object::Handle(Array::Cast(option).At(0)); | 
| 69 |         if (type.IsType()) { | 
| 70 |           return Type::Cast(type).type_class_id(); | 
| 71 |         } | 
| 72 |       } | 
| 73 |     } | 
| 74 |   } | 
| 75 |  | 
| 76 |   return kDynamicCid; | 
| 77 | } | 
| 78 |  | 
| 79 | bool MethodRecognizer::HasNonNullableResultTypeFromPragma( | 
| 80 |     const Object& function_or_field) { | 
| 81 |   auto T = Thread::Current(); | 
| 82 |   auto Z = T->zone(); | 
| 83 |   auto& option = Object::Handle(Z); | 
| 84 |   if (Library::FindPragma(T, /*only_core=*/true, function_or_field, | 
| 85 |                           Symbols::vm_non_nullable_result_type(), &option)) { | 
| 86 |     return true; | 
| 87 |   } | 
| 88 |  | 
| 89 |   // If nothing said otherwise, the return type is nullable. | 
| 90 |   return false; | 
| 91 | } | 
| 92 |  | 
| 93 | intptr_t MethodRecognizer::MethodKindToReceiverCid(Kind kind) { | 
| 94 |   switch (kind) { | 
| 95 |     case kImmutableArrayGetIndexed: | 
| 96 |       return kImmutableArrayCid; | 
| 97 |  | 
| 98 |     case kObjectArrayGetIndexed: | 
| 99 |     case kObjectArraySetIndexed: | 
| 100 |     case kObjectArraySetIndexedUnchecked: | 
| 101 |       return kArrayCid; | 
| 102 |  | 
| 103 |     case kGrowableArrayGetIndexed: | 
| 104 |     case kGrowableArraySetIndexed: | 
| 105 |     case kGrowableArraySetIndexedUnchecked: | 
| 106 |       return kGrowableObjectArrayCid; | 
| 107 |  | 
| 108 |     case kFloat32ArrayGetIndexed: | 
| 109 |     case kFloat32ArraySetIndexed: | 
| 110 |       return kTypedDataFloat32ArrayCid; | 
| 111 |  | 
| 112 |     case kFloat64ArrayGetIndexed: | 
| 113 |     case kFloat64ArraySetIndexed: | 
| 114 |       return kTypedDataFloat64ArrayCid; | 
| 115 |  | 
| 116 |     case kInt8ArrayGetIndexed: | 
| 117 |     case kInt8ArraySetIndexed: | 
| 118 |       return kTypedDataInt8ArrayCid; | 
| 119 |  | 
| 120 |     case kUint8ArrayGetIndexed: | 
| 121 |     case kUint8ArraySetIndexed: | 
| 122 |       return kTypedDataUint8ArrayCid; | 
| 123 |  | 
| 124 |     case kUint8ClampedArrayGetIndexed: | 
| 125 |     case kUint8ClampedArraySetIndexed: | 
| 126 |       return kTypedDataUint8ClampedArrayCid; | 
| 127 |  | 
| 128 |     case kExternalUint8ArrayGetIndexed: | 
| 129 |     case kExternalUint8ArraySetIndexed: | 
| 130 |       return kExternalTypedDataUint8ArrayCid; | 
| 131 |  | 
| 132 |     case kExternalUint8ClampedArrayGetIndexed: | 
| 133 |     case kExternalUint8ClampedArraySetIndexed: | 
| 134 |       return kExternalTypedDataUint8ClampedArrayCid; | 
| 135 |  | 
| 136 |     case kInt16ArrayGetIndexed: | 
| 137 |     case kInt16ArraySetIndexed: | 
| 138 |       return kTypedDataInt16ArrayCid; | 
| 139 |  | 
| 140 |     case kUint16ArrayGetIndexed: | 
| 141 |     case kUint16ArraySetIndexed: | 
| 142 |       return kTypedDataUint16ArrayCid; | 
| 143 |  | 
| 144 |     case kInt32ArrayGetIndexed: | 
| 145 |     case kInt32ArraySetIndexed: | 
| 146 |       return kTypedDataInt32ArrayCid; | 
| 147 |  | 
| 148 |     case kUint32ArrayGetIndexed: | 
| 149 |     case kUint32ArraySetIndexed: | 
| 150 |       return kTypedDataUint32ArrayCid; | 
| 151 |  | 
| 152 |     case kInt64ArrayGetIndexed: | 
| 153 |     case kInt64ArraySetIndexed: | 
| 154 |       return kTypedDataInt64ArrayCid; | 
| 155 |  | 
| 156 |     case kUint64ArrayGetIndexed: | 
| 157 |     case kUint64ArraySetIndexed: | 
| 158 |       return kTypedDataUint64ArrayCid; | 
| 159 |  | 
| 160 |     case kFloat32x4ArrayGetIndexed: | 
| 161 |     case kFloat32x4ArraySetIndexed: | 
| 162 |       return kTypedDataFloat32x4ArrayCid; | 
| 163 |  | 
| 164 |     case kInt32x4ArrayGetIndexed: | 
| 165 |     case kInt32x4ArraySetIndexed: | 
| 166 |       return kTypedDataInt32x4ArrayCid; | 
| 167 |  | 
| 168 |     case kFloat64x2ArrayGetIndexed: | 
| 169 |     case kFloat64x2ArraySetIndexed: | 
| 170 |       return kTypedDataFloat64x2ArrayCid; | 
| 171 |  | 
| 172 |     default: | 
| 173 |       break; | 
| 174 |   } | 
| 175 |   UNREACHABLE(); | 
| 176 |   return kIllegalCid; | 
| 177 | } | 
| 178 |  | 
| 179 | static const struct { | 
| 180 |   const char* const class_name; | 
| 181 |   const char* const function_name; | 
| 182 |   const char* const enum_name; | 
| 183 |   const uint32_t fp; | 
| 184 | } recognized_methods[MethodRecognizer::kNumRecognizedMethods] = { | 
| 185 |     {"" , "" , "Unknown" , 0}, | 
| 186 | #define RECOGNIZE_METHOD(class_name, function_name, enum_name, fp)             \ | 
| 187 |   {"" #class_name, "" #function_name, #enum_name, fp}, | 
| 188 |     RECOGNIZED_LIST(RECOGNIZE_METHOD) | 
| 189 | #undef RECOGNIZE_METHOD | 
| 190 | }; | 
| 191 |  | 
| 192 | const char* MethodRecognizer::KindToCString(Kind kind) { | 
| 193 |   if (kind >= kUnknown && kind < kNumRecognizedMethods) | 
| 194 |     return recognized_methods[kind].enum_name; | 
| 195 |   return "?" ; | 
| 196 | } | 
| 197 |  | 
| 198 | void MethodRecognizer::InitializeState() { | 
| 199 |   GrowableArray<Library*> libs(3); | 
| 200 |   Libraries(&libs); | 
| 201 |   Function& func = Function::Handle(); | 
| 202 |   bool fingerprints_match = true; | 
| 203 |  | 
| 204 |   for (intptr_t i = 1; i < MethodRecognizer::kNumRecognizedMethods; i++) { | 
| 205 |     const MethodRecognizer::Kind kind = static_cast<MethodRecognizer::Kind>(i); | 
| 206 |     func = Library::GetFunction(libs, recognized_methods[i].class_name, | 
| 207 |                                 recognized_methods[i].function_name); | 
| 208 |     if (!func.IsNull()) { | 
| 209 |       fingerprints_match = | 
| 210 |           func.CheckSourceFingerprint(recognized_methods[i].fp) && | 
| 211 |           fingerprints_match; | 
| 212 |       func.set_recognized_kind(kind); | 
| 213 |       switch (kind) { | 
| 214 | #define RECOGNIZE_METHOD(class_name, function_name, enum_name, fp)             \ | 
| 215 |   case MethodRecognizer::k##enum_name:                                         \ | 
| 216 |     func.reset_unboxed_parameters_and_return();                                \ | 
| 217 |     break; | 
| 218 |         ALL_INTRINSICS_LIST(RECOGNIZE_METHOD) | 
| 219 | #undef RECOGNIZE_METHOD | 
| 220 |         default: | 
| 221 |           break; | 
| 222 |       } | 
| 223 |     } else if (!FLAG_precompiled_mode) { | 
| 224 |       fingerprints_match = false; | 
| 225 |       OS::PrintErr("Missing %s::%s\n" , recognized_methods[i].class_name, | 
| 226 |                    recognized_methods[i].function_name); | 
| 227 |     } | 
| 228 |   } | 
| 229 |  | 
| 230 | #define SET_FUNCTION_BIT(class_name, function_name, dest, fp, setter, value)   \ | 
| 231 |   func = Library::GetFunction(libs, #class_name, #function_name);              \ | 
| 232 |   if (!func.IsNull()) {                                                        \ | 
| 233 |     fingerprints_match =                                                       \ | 
| 234 |         func.CheckSourceFingerprint(fp) && fingerprints_match;                 \ | 
| 235 |     func.setter(value);                                                        \ | 
| 236 |   } else if (!FLAG_precompiled_mode) {                                         \ | 
| 237 |     OS::PrintErr("Missing %s::%s\n", #class_name, #function_name);             \ | 
| 238 |     fingerprints_match = false;                                                \ | 
| 239 |   } | 
| 240 |  | 
| 241 | #define SET_IS_POLYMORPHIC_TARGET(class_name, function_name, dest, fp)         \ | 
| 242 |   SET_FUNCTION_BIT(class_name, function_name, dest, fp,                        \ | 
| 243 |                    set_is_polymorphic_target, true) | 
| 244 |  | 
| 245 |   POLYMORPHIC_TARGET_LIST(SET_IS_POLYMORPHIC_TARGET); | 
| 246 |  | 
| 247 | #undef SET_RECOGNIZED_KIND | 
| 248 | #undef SET_IS_POLYMORPHIC_TARGET | 
| 249 | #undef SET_FUNCTION_BIT | 
| 250 |  | 
| 251 |   if (!fingerprints_match) { | 
| 252 |     FATAL( | 
| 253 |         "FP mismatch while recognizing methods. If the behavior of "  | 
| 254 |         "these functions has changed, then changes are also needed in "  | 
| 255 |         "the VM's compiler. Otherwise the fingerprint can simply be "  | 
| 256 |         "updated in recognized_methods_list.h\n" ); | 
| 257 |   } | 
| 258 | } | 
| 259 |  | 
| 260 | void MethodRecognizer::Libraries(GrowableArray<Library*>* libs) { | 
| 261 |   libs->Add(&Library::ZoneHandle(Library::CoreLibrary())); | 
| 262 |   libs->Add(&Library::ZoneHandle(Library::CollectionLibrary())); | 
| 263 |   libs->Add(&Library::ZoneHandle(Library::MathLibrary())); | 
| 264 |   libs->Add(&Library::ZoneHandle(Library::TypedDataLibrary())); | 
| 265 |   libs->Add(&Library::ZoneHandle(Library::ConvertLibrary())); | 
| 266 |   libs->Add(&Library::ZoneHandle(Library::InternalLibrary())); | 
| 267 |   libs->Add(&Library::ZoneHandle(Library::DeveloperLibrary())); | 
| 268 |   libs->Add(&Library::ZoneHandle(Library::AsyncLibrary())); | 
| 269 |   libs->Add(&Library::ZoneHandle(Library::FfiLibrary())); | 
| 270 | } | 
| 271 |  | 
| 272 | static Token::Kind RecognizeTokenKindHelper(const String& name) { | 
| 273 |   if (name.raw() == Symbols::Plus().raw()) { | 
| 274 |     return Token::kADD; | 
| 275 |   } else if (name.raw() == Symbols::Minus().raw()) { | 
| 276 |     return Token::kSUB; | 
| 277 |   } else if (name.raw() == Symbols::Star().raw()) { | 
| 278 |     return Token::kMUL; | 
| 279 |   } else if (name.raw() == Symbols::Slash().raw()) { | 
| 280 |     return Token::kDIV; | 
| 281 |   } else if (name.raw() == Symbols::TruncDivOperator().raw()) { | 
| 282 |     return Token::kTRUNCDIV; | 
| 283 |   } else if (name.raw() == Symbols::Percent().raw()) { | 
| 284 |     return Token::kMOD; | 
| 285 |   } else if (name.raw() == Symbols::BitOr().raw()) { | 
| 286 |     return Token::kBIT_OR; | 
| 287 |   } else if (name.raw() == Symbols::Ampersand().raw()) { | 
| 288 |     return Token::kBIT_AND; | 
| 289 |   } else if (name.raw() == Symbols::Caret().raw()) { | 
| 290 |     return Token::kBIT_XOR; | 
| 291 |   } else if (name.raw() == Symbols::LeftShiftOperator().raw()) { | 
| 292 |     return Token::kSHL; | 
| 293 |   } else if (name.raw() == Symbols::RightShiftOperator().raw()) { | 
| 294 |     return Token::kSHR; | 
| 295 |   } else if (name.raw() == Symbols::Tilde().raw()) { | 
| 296 |     return Token::kBIT_NOT; | 
| 297 |   } else if (name.raw() == Symbols::UnaryMinus().raw()) { | 
| 298 |     return Token::kNEGATE; | 
| 299 |   } else if (name.raw() == Symbols::EqualOperator().raw()) { | 
| 300 |     return Token::kEQ; | 
| 301 |   } else if (name.raw() == Symbols::Token(Token::kNE).raw()) { | 
| 302 |     return Token::kNE; | 
| 303 |   } else if (name.raw() == Symbols::LAngleBracket().raw()) { | 
| 304 |     return Token::kLT; | 
| 305 |   } else if (name.raw() == Symbols::RAngleBracket().raw()) { | 
| 306 |     return Token::kGT; | 
| 307 |   } else if (name.raw() == Symbols::LessEqualOperator().raw()) { | 
| 308 |     return Token::kLTE; | 
| 309 |   } else if (name.raw() == Symbols::GreaterEqualOperator().raw()) { | 
| 310 |     return Token::kGTE; | 
| 311 |   } else if (Field::IsGetterName(name)) { | 
| 312 |     return Token::kGET; | 
| 313 |   } else if (Field::IsSetterName(name)) { | 
| 314 |     return Token::kSET; | 
| 315 |   } | 
| 316 |   return Token::kILLEGAL; | 
| 317 | } | 
| 318 |  | 
| 319 | Token::Kind MethodTokenRecognizer::RecognizeTokenKind(const String& name) { | 
| 320 |   ASSERT(name.IsSymbol()); | 
| 321 |   if (Function::IsDynamicInvocationForwarderName(name)) { | 
| 322 |     Thread* thread = Thread::Current(); | 
| 323 |     const auto& demangled_name = String::Handle( | 
| 324 |         thread->zone(), Function::DemangleDynamicInvocationForwarderName(name)); | 
| 325 |     return RecognizeTokenKindHelper(demangled_name); | 
| 326 |   } else { | 
| 327 |     return RecognizeTokenKindHelper(name); | 
| 328 |   } | 
| 329 | } | 
| 330 |  | 
| 331 | #define RECOGNIZE_FACTORY(symbol, class_name, constructor_name, cid, fp)       \ | 
| 332 |   {Symbols::k##symbol##Id, cid, fp, #symbol ", " #cid},  // NOLINT | 
| 333 |  | 
| 334 | static struct { | 
| 335 |   const intptr_t symbol_id; | 
| 336 |   const intptr_t cid; | 
| 337 |   const uint32_t finger_print; | 
| 338 |   const char* const name; | 
| 339 | } factory_recognizer_list[] = {RECOGNIZED_LIST_FACTORY_LIST(RECOGNIZE_FACTORY){ | 
| 340 |     Symbols::kIllegal, -1, 0, NULL}}; | 
| 341 |  | 
| 342 | #undef RECOGNIZE_FACTORY | 
| 343 |  | 
| 344 | intptr_t FactoryRecognizer::ResultCid(const Function& factory) { | 
| 345 |   ASSERT(factory.IsFactory()); | 
| 346 |   const Class& function_class = Class::Handle(factory.Owner()); | 
| 347 |   const Library& lib = Library::Handle(function_class.library()); | 
| 348 |   ASSERT((lib.raw() == Library::CoreLibrary()) || | 
| 349 |          (lib.raw() == Library::TypedDataLibrary())); | 
| 350 |   const String& factory_name = String::Handle(factory.name()); | 
| 351 |   for (intptr_t i = 0; | 
| 352 |        factory_recognizer_list[i].symbol_id != Symbols::kIllegal; i++) { | 
| 353 |     if (String::EqualsIgnoringPrivateKey( | 
| 354 |             factory_name, | 
| 355 |             Symbols::Symbol(factory_recognizer_list[i].symbol_id))) { | 
| 356 |       return factory_recognizer_list[i].cid; | 
| 357 |     } | 
| 358 |   } | 
| 359 |   return kDynamicCid; | 
| 360 | } | 
| 361 |  | 
| 362 | intptr_t FactoryRecognizer::GetResultCidOfListFactory(Zone* zone, | 
| 363 |                                                       const Function& function, | 
| 364 |                                                       intptr_t argument_count) { | 
| 365 |   if (!function.IsFactory()) { | 
| 366 |     return kDynamicCid; | 
| 367 |   } | 
| 368 |  | 
| 369 |   const Class& owner = Class::Handle(zone, function.Owner()); | 
| 370 |   if ((owner.library() != Library::CoreLibrary()) && | 
| 371 |       (owner.library() != Library::TypedDataLibrary())) { | 
| 372 |     return kDynamicCid; | 
| 373 |   } | 
| 374 |  | 
| 375 |   if (owner.Name() == Symbols::List().raw()) { | 
| 376 |     if (function.name() == Symbols::ListFactory().raw()) { | 
| 377 |       ASSERT(argument_count == 1 || argument_count == 2); | 
| 378 |       return (argument_count == 1) ? kGrowableObjectArrayCid : kArrayCid; | 
| 379 |     } else if (function.name() == Symbols::ListFilledFactory().raw()) { | 
| 380 |       ASSERT(argument_count == 3 || argument_count == 4); | 
| 381 |       return (argument_count == 3) ? kArrayCid : kDynamicCid; | 
| 382 |     } | 
| 383 |   } | 
| 384 |  | 
| 385 |   return ResultCid(function); | 
| 386 | } | 
| 387 |  | 
| 388 | }  // namespace dart | 
| 389 |  |