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
12namespace dart {
13
14intptr_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
26intptr_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
79bool 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
93intptr_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
179static 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
192const char* MethodRecognizer::KindToCString(Kind kind) {
193 if (kind >= kUnknown && kind < kNumRecognizedMethods)
194 return recognized_methods[kind].enum_name;
195 return "?";
196}
197
198void 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
260void 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
272static 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
319Token::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
334static 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
344intptr_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
362intptr_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