1 | // Copyright (c) 2015, 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 | #ifndef RUNTIME_VM_COMPILER_AOT_PRECOMPILER_H_ |
6 | #define RUNTIME_VM_COMPILER_AOT_PRECOMPILER_H_ |
7 | |
8 | #if defined(DART_PRECOMPILED_RUNTIME) |
9 | #error "AOT runtime should not use compiler sources (including header files)" |
10 | #endif // defined(DART_PRECOMPILED_RUNTIME) |
11 | |
12 | #include "vm/allocation.h" |
13 | #include "vm/compiler/aot/dispatch_table_generator.h" |
14 | #include "vm/compiler/assembler/assembler.h" |
15 | #include "vm/hash_map.h" |
16 | #include "vm/hash_table.h" |
17 | #include "vm/object.h" |
18 | #include "vm/symbols.h" |
19 | |
20 | namespace dart { |
21 | |
22 | // Forward declarations. |
23 | class Class; |
24 | class Error; |
25 | class Field; |
26 | class Function; |
27 | class GrowableObjectArray; |
28 | class String; |
29 | class Precompiler; |
30 | class FlowGraph; |
31 | class PrecompilerTracer; |
32 | |
33 | class TableSelectorKeyValueTrait { |
34 | public: |
35 | // Typedefs needed for the DirectChainedHashMap template. |
36 | typedef int32_t Key; |
37 | typedef int32_t Value; |
38 | typedef int32_t Pair; |
39 | |
40 | static Key KeyOf(Pair kv) { return kv; } |
41 | |
42 | static Value ValueOf(Pair kv) { return kv; } |
43 | |
44 | static inline intptr_t Hashcode(Key key) { return key; } |
45 | |
46 | static inline bool IsKeyEqual(Pair pair, Key key) { return pair == key; } |
47 | }; |
48 | |
49 | typedef DirectChainedHashMap<TableSelectorKeyValueTrait> TableSelectorSet; |
50 | |
51 | class SymbolKeyValueTrait { |
52 | public: |
53 | // Typedefs needed for the DirectChainedHashMap template. |
54 | typedef const String* Key; |
55 | typedef const String* Value; |
56 | typedef const String* Pair; |
57 | |
58 | static Key KeyOf(Pair kv) { return kv; } |
59 | |
60 | static Value ValueOf(Pair kv) { return kv; } |
61 | |
62 | static inline intptr_t Hashcode(Key key) { return key->Hash(); } |
63 | |
64 | static inline bool IsKeyEqual(Pair pair, Key key) { |
65 | return pair->raw() == key->raw(); |
66 | } |
67 | }; |
68 | |
69 | typedef DirectChainedHashMap<SymbolKeyValueTrait> SymbolSet; |
70 | |
71 | // Traits for the HashTable template. |
72 | struct FunctionKeyTraits { |
73 | static uint32_t Hash(const Object& key) { return Function::Cast(key).Hash(); } |
74 | static const char* Name() { return "FunctionKeyTraits" ; } |
75 | static bool IsMatch(const Object& x, const Object& y) { |
76 | return x.raw() == y.raw(); |
77 | } |
78 | static bool ReportStats() { return false; } |
79 | }; |
80 | |
81 | typedef UnorderedHashSet<FunctionKeyTraits> FunctionSet; |
82 | |
83 | class FieldKeyValueTrait { |
84 | public: |
85 | // Typedefs needed for the DirectChainedHashMap template. |
86 | typedef const Field* Key; |
87 | typedef const Field* Value; |
88 | typedef const Field* Pair; |
89 | |
90 | static Key KeyOf(Pair kv) { return kv; } |
91 | |
92 | static Value ValueOf(Pair kv) { return kv; } |
93 | |
94 | static inline intptr_t Hashcode(Key key) { |
95 | const TokenPosition token_pos = key->token_pos(); |
96 | if (token_pos.IsReal()) { |
97 | return token_pos.value(); |
98 | } |
99 | return key->binary_declaration_offset(); |
100 | } |
101 | |
102 | static inline bool IsKeyEqual(Pair pair, Key key) { |
103 | return pair->raw() == key->raw(); |
104 | } |
105 | }; |
106 | |
107 | typedef DirectChainedHashMap<FieldKeyValueTrait> FieldSet; |
108 | |
109 | class ClassKeyValueTrait { |
110 | public: |
111 | // Typedefs needed for the DirectChainedHashMap template. |
112 | typedef const Class* Key; |
113 | typedef const Class* Value; |
114 | typedef const Class* Pair; |
115 | |
116 | static Key KeyOf(Pair kv) { return kv; } |
117 | |
118 | static Value ValueOf(Pair kv) { return kv; } |
119 | |
120 | static inline intptr_t Hashcode(Key key) { return key->token_pos().value(); } |
121 | |
122 | static inline bool IsKeyEqual(Pair pair, Key key) { |
123 | return pair->raw() == key->raw(); |
124 | } |
125 | }; |
126 | |
127 | typedef DirectChainedHashMap<ClassKeyValueTrait> ClassSet; |
128 | |
129 | class AbstractTypeKeyValueTrait { |
130 | public: |
131 | // Typedefs needed for the DirectChainedHashMap template. |
132 | typedef const AbstractType* Key; |
133 | typedef const AbstractType* Value; |
134 | typedef const AbstractType* Pair; |
135 | |
136 | static Key KeyOf(Pair kv) { return kv; } |
137 | |
138 | static Value ValueOf(Pair kv) { return kv; } |
139 | |
140 | static inline intptr_t Hashcode(Key key) { return key->Hash(); } |
141 | |
142 | static inline bool IsKeyEqual(Pair pair, Key key) { |
143 | return pair->raw() == key->raw(); |
144 | } |
145 | }; |
146 | |
147 | typedef DirectChainedHashMap<AbstractTypeKeyValueTrait> AbstractTypeSet; |
148 | |
149 | class TypeParameterKeyValueTrait { |
150 | public: |
151 | // Typedefs needed for the DirectChainedHashMap template. |
152 | typedef const TypeParameter* Key; |
153 | typedef const TypeParameter* Value; |
154 | typedef const TypeParameter* Pair; |
155 | |
156 | static Key KeyOf(Pair kv) { return kv; } |
157 | |
158 | static Value ValueOf(Pair kv) { return kv; } |
159 | |
160 | static inline intptr_t Hashcode(Key key) { return key->Hash(); } |
161 | |
162 | static inline bool IsKeyEqual(Pair pair, Key key) { |
163 | return pair->raw() == key->raw(); |
164 | } |
165 | }; |
166 | |
167 | typedef DirectChainedHashMap<TypeParameterKeyValueTrait> TypeParameterSet; |
168 | |
169 | class TypeArgumentsKeyValueTrait { |
170 | public: |
171 | // Typedefs needed for the DirectChainedHashMap template. |
172 | typedef const TypeArguments* Key; |
173 | typedef const TypeArguments* Value; |
174 | typedef const TypeArguments* Pair; |
175 | |
176 | static Key KeyOf(Pair kv) { return kv; } |
177 | |
178 | static Value ValueOf(Pair kv) { return kv; } |
179 | |
180 | static inline intptr_t Hashcode(Key key) { return key->Hash(); } |
181 | |
182 | static inline bool IsKeyEqual(Pair pair, Key key) { |
183 | return pair->raw() == key->raw(); |
184 | } |
185 | }; |
186 | |
187 | typedef DirectChainedHashMap<TypeArgumentsKeyValueTrait> TypeArgumentsSet; |
188 | |
189 | class InstanceKeyValueTrait { |
190 | public: |
191 | // Typedefs needed for the DirectChainedHashMap template. |
192 | typedef const Instance* Key; |
193 | typedef const Instance* Value; |
194 | typedef const Instance* Pair; |
195 | |
196 | static Key KeyOf(Pair kv) { return kv; } |
197 | |
198 | static Value ValueOf(Pair kv) { return kv; } |
199 | |
200 | static inline intptr_t Hashcode(Key key) { return key->GetClassId(); } |
201 | |
202 | static inline bool IsKeyEqual(Pair pair, Key key) { |
203 | return pair->raw() == key->raw(); |
204 | } |
205 | }; |
206 | |
207 | typedef DirectChainedHashMap<InstanceKeyValueTrait> InstanceSet; |
208 | |
209 | class Precompiler : public ValueObject { |
210 | public: |
211 | static ErrorPtr CompileAll(); |
212 | |
213 | static ErrorPtr CompileFunction(Precompiler* precompiler, |
214 | Thread* thread, |
215 | Zone* zone, |
216 | const Function& function); |
217 | |
218 | // Returns true if get:runtimeType is not overloaded by any class. |
219 | bool get_runtime_type_is_unique() const { |
220 | return get_runtime_type_is_unique_; |
221 | } |
222 | |
223 | compiler::ObjectPoolBuilder* global_object_pool_builder() { |
224 | ASSERT(FLAG_use_bare_instructions); |
225 | return &global_object_pool_builder_; |
226 | } |
227 | |
228 | compiler::SelectorMap* selector_map() { |
229 | ASSERT(FLAG_use_bare_instructions && FLAG_use_table_dispatch); |
230 | return dispatch_table_generator_->selector_map(); |
231 | } |
232 | |
233 | void* il_serialization_stream() const { return il_serialization_stream_; } |
234 | |
235 | static Precompiler* Instance() { return singleton_; } |
236 | |
237 | void AddField(const Field& field); |
238 | void AddTableSelector(const compiler::TableSelector* selector); |
239 | |
240 | enum class Phase { |
241 | kPreparation, |
242 | kCompilingConstructorsForInstructionCounts, |
243 | kFixpointCodeGeneration, |
244 | kDone, |
245 | }; |
246 | |
247 | Phase phase() const { return phase_; } |
248 | |
249 | bool is_tracing() const { return is_tracing_; } |
250 | |
251 | private: |
252 | static Precompiler* singleton_; |
253 | |
254 | // Scope which activates machine readable precompiler tracing if tracer |
255 | // is available. |
256 | class TracingScope : public ValueObject { |
257 | public: |
258 | explicit TracingScope(Precompiler* precompiler) |
259 | : precompiler_(precompiler), was_tracing_(precompiler->is_tracing_) { |
260 | precompiler->is_tracing_ = (precompiler->tracer_ != nullptr); |
261 | } |
262 | |
263 | ~TracingScope() { precompiler_->is_tracing_ = was_tracing_; } |
264 | |
265 | private: |
266 | Precompiler* const precompiler_; |
267 | const bool was_tracing_; |
268 | }; |
269 | |
270 | explicit Precompiler(Thread* thread); |
271 | ~Precompiler(); |
272 | |
273 | void DoCompileAll(); |
274 | void AddRoots(); |
275 | void AddAnnotatedRoots(); |
276 | void Iterate(); |
277 | |
278 | void AddType(const AbstractType& type); |
279 | void AddTypesOf(const Class& cls); |
280 | void AddTypesOf(const Function& function); |
281 | void AddTypeArguments(const TypeArguments& args); |
282 | void AddCalleesOf(const Function& function, intptr_t gop_offset); |
283 | void AddCalleesOfHelper(const Object& entry, |
284 | String* temp_selector, |
285 | Class* temp_cls); |
286 | void AddConstObject(const class Instance& instance); |
287 | void AddClosureCall(const String& selector, |
288 | const Array& arguments_descriptor); |
289 | void AddFunction(const Function& function, bool retain = true); |
290 | void AddInstantiatedClass(const Class& cls); |
291 | void AddSelector(const String& selector); |
292 | bool IsSent(const String& selector); |
293 | bool IsHitByTableSelector(const Function& function); |
294 | bool MustRetainFunction(const Function& function); |
295 | |
296 | void ProcessFunction(const Function& function); |
297 | void CheckForNewDynamicFunctions(); |
298 | void CollectCallbackFields(); |
299 | |
300 | void AttachOptimizedTypeTestingStub(); |
301 | |
302 | void TraceForRetainedFunctions(); |
303 | void FinalizeDispatchTable(); |
304 | void ReplaceFunctionPCRelativeCallEntries(); |
305 | void DropFunctions(); |
306 | void DropFields(); |
307 | void TraceTypesFromRetainedClasses(); |
308 | void DropTypes(); |
309 | void DropTypeParameters(); |
310 | void DropTypeArguments(); |
311 | void DropMetadata(); |
312 | void DropLibraryEntries(); |
313 | void DropClasses(); |
314 | void DropLibraries(); |
315 | |
316 | DEBUG_ONLY(FunctionPtr FindUnvisitedRetainedFunction()); |
317 | |
318 | void Obfuscate(); |
319 | |
320 | void CollectDynamicFunctionNames(); |
321 | |
322 | void PrecompileStaticInitializers(); |
323 | void PrecompileConstructors(); |
324 | |
325 | void FinalizeAllClasses(); |
326 | |
327 | void set_il_serialization_stream(void* file) { |
328 | il_serialization_stream_ = file; |
329 | } |
330 | |
331 | Thread* thread() const { return thread_; } |
332 | Zone* zone() const { return zone_; } |
333 | Isolate* isolate() const { return isolate_; } |
334 | |
335 | Thread* thread_; |
336 | Zone* zone_; |
337 | Isolate* isolate_; |
338 | |
339 | bool changed_; |
340 | bool retain_root_library_caches_; |
341 | intptr_t function_count_; |
342 | intptr_t class_count_; |
343 | intptr_t selector_count_; |
344 | intptr_t dropped_function_count_; |
345 | intptr_t dropped_field_count_; |
346 | intptr_t dropped_class_count_; |
347 | intptr_t dropped_typearg_count_; |
348 | intptr_t dropped_type_count_; |
349 | intptr_t dropped_typeparam_count_; |
350 | intptr_t dropped_library_count_; |
351 | |
352 | compiler::ObjectPoolBuilder global_object_pool_builder_; |
353 | GrowableObjectArray& libraries_; |
354 | const GrowableObjectArray& pending_functions_; |
355 | SymbolSet sent_selectors_; |
356 | FunctionSet seen_functions_; |
357 | FunctionSet possibly_retained_functions_; |
358 | FieldSet fields_to_retain_; |
359 | FunctionSet functions_to_retain_; |
360 | ClassSet classes_to_retain_; |
361 | TypeArgumentsSet typeargs_to_retain_; |
362 | AbstractTypeSet types_to_retain_; |
363 | TypeParameterSet typeparams_to_retain_; |
364 | InstanceSet consts_to_retain_; |
365 | TableSelectorSet seen_table_selectors_; |
366 | Error& error_; |
367 | |
368 | compiler::DispatchTableGenerator* dispatch_table_generator_; |
369 | |
370 | bool get_runtime_type_is_unique_; |
371 | void* il_serialization_stream_; |
372 | |
373 | Phase phase_ = Phase::kPreparation; |
374 | PrecompilerTracer* tracer_ = nullptr; |
375 | bool is_tracing_ = false; |
376 | }; |
377 | |
378 | class FunctionsTraits { |
379 | public: |
380 | static const char* Name() { return "FunctionsTraits" ; } |
381 | static bool ReportStats() { return false; } |
382 | |
383 | static bool IsMatch(const Object& a, const Object& b) { |
384 | return String::Cast(a).raw() == String::Cast(b).raw(); |
385 | } |
386 | static uword Hash(const Object& obj) { return String::Cast(obj).Hash(); } |
387 | }; |
388 | |
389 | typedef UnorderedHashMap<FunctionsTraits> UniqueFunctionsMap; |
390 | |
391 | #if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32) |
392 | // ObfuscationMap maps Strings to Strings. |
393 | class ObfuscationMapTraits { |
394 | public: |
395 | static const char* Name() { return "ObfuscationMapTraits" ; } |
396 | static bool ReportStats() { return false; } |
397 | |
398 | // Only for non-descriptor lookup and table expansion. |
399 | static bool IsMatch(const Object& a, const Object& b) { |
400 | return a.raw() == b.raw(); |
401 | } |
402 | |
403 | static uword Hash(const Object& key) { return String::Cast(key).Hash(); } |
404 | }; |
405 | typedef UnorderedHashMap<ObfuscationMapTraits> ObfuscationMap; |
406 | |
407 | // Obfuscator is a helper class that is responsible for obfuscating |
408 | // identifiers when obfuscation is enabled via isolate flags. |
409 | // |
410 | class Obfuscator : public ValueObject { |
411 | public: |
412 | // Create Obfuscator for the given |thread|, with the given |private_key|. |
413 | // This private key will be used when obfuscating private identifiers |
414 | // (those starting with '_'). |
415 | // |
416 | // If obfuscation is enabled constructor will restore obfuscation state |
417 | // from ObjectStore::obfuscation_map() |
418 | // |
419 | // Note: only a single instance of obfuscator should exist at any given |
420 | // moment on the stack because Obfuscator takes ownership of obfuscation |
421 | // map. ObjectStore::obfuscation_map() will only be updated when |
422 | // this Obfuscator is destroyed. |
423 | Obfuscator(Thread* thread, const String& private_key); |
424 | |
425 | // If obfuscation is enabled - commit accumulated renames to ObjectStore. |
426 | ~Obfuscator(); |
427 | |
428 | // If obfuscation is enabled return a rename for the given |name|, |
429 | // otherwise it is a no-op. |
430 | // |
431 | // Note: |name| *must* be a Symbol. |
432 | // |
433 | // By default renames are aware about mangling scheme used for private names: |
434 | // '_ident@key' and '_ident' will be renamed consistently. If such |
435 | // interpretation is undesirable e.g. it is known that name does not |
436 | // contain a private key suffix or name is not a Dart identifier at all |
437 | // then this function should be called with |atomic| set to true. |
438 | // |
439 | // Note: if obfuscator was created with private_key then all |
440 | // renames *must* be atomic. |
441 | // |
442 | // This method is guaranteed to return the same value for the same |
443 | // input and it always preserves leading '_' even for atomic renames. |
444 | StringPtr Rename(const String& name, bool atomic = false) { |
445 | if (state_ == NULL) { |
446 | return name.raw(); |
447 | } |
448 | |
449 | return state_->RenameImpl(name, atomic); |
450 | } |
451 | |
452 | // Given a sequence of obfuscated identifiers deobfuscate it. |
453 | // |
454 | // This method is only used by parser when resolving conditional imports |
455 | // because it needs deobfuscated names to lookup in the environment. |
456 | // |
457 | // Note: this operation is not optimized because is very infrequent. |
458 | static void Deobfuscate(Thread* thread, const GrowableObjectArray& pieces); |
459 | |
460 | // Serialize renaming map as a malloced array of strings. |
461 | static const char** SerializeMap(Thread* thread); |
462 | |
463 | void PreventRenaming(const char* name); |
464 | void PreventRenaming(const String& name) { state_->PreventRenaming(name); } |
465 | |
466 | private: |
467 | // Populate renaming map with names that should have identity renaming. |
468 | // (or in other words: with those names that should not be renamed). |
469 | void InitializeRenamingMap(Isolate* isolate); |
470 | |
471 | // ObjectStore::obfuscation_map() is an Array with two elements: |
472 | // first element is the last used rename and the second element is |
473 | // renaming map. |
474 | static const intptr_t kSavedStateNameIndex = 0; |
475 | static const intptr_t kSavedStateRenamesIndex = 1; |
476 | static const intptr_t kSavedStateSize = 2; |
477 | |
478 | static ArrayPtr GetRenamesFromSavedState(const Array& saved_state) { |
479 | Array& renames = Array::Handle(); |
480 | renames ^= saved_state.At(kSavedStateRenamesIndex); |
481 | return renames.raw(); |
482 | } |
483 | |
484 | static StringPtr GetNameFromSavedState(const Array& saved_state) { |
485 | String& name = String::Handle(); |
486 | name ^= saved_state.At(kSavedStateNameIndex); |
487 | return name.raw(); |
488 | } |
489 | |
490 | class ObfuscationState : public ZoneAllocated { |
491 | public: |
492 | ObfuscationState(Thread* thread, |
493 | const Array& saved_state, |
494 | const String& private_key) |
495 | : thread_(thread), |
496 | saved_state_(saved_state), |
497 | renames_(GetRenamesFromSavedState(saved_state)), |
498 | private_key_(private_key), |
499 | string_(String::Handle(thread->zone())), |
500 | renamed_(String::Handle(thread->zone())) { |
501 | memset(name_, 0, sizeof(name_)); |
502 | |
503 | // Restore last used rename. |
504 | string_ = GetNameFromSavedState(saved_state); |
505 | if (!string_.IsNull()) { |
506 | string_.ToUTF8(reinterpret_cast<uint8_t*>(name_), sizeof(name_)); |
507 | } |
508 | } |
509 | |
510 | void SaveState(); |
511 | |
512 | // Return a rename for the given |name|. |
513 | // |
514 | // Note: |name| *must* be a Symbol. |
515 | // |
516 | // By default renames are aware about mangling scheme used for private |
517 | // names, getters and setters: '_ident@key', 'get:_ident@key' and |
518 | // '_ident' will be renamed consistently. If such interpretation is |
519 | // undesirable e.g. it is known that name does not contain a private |
520 | // key suffix or name is not a Dart identifier at all |
521 | // then this function should be called with |atomic| set to true. |
522 | // |
523 | // Note: if obfuscator was created with private_key then all |
524 | // renames *must* be atomic. |
525 | // |
526 | // This method is guaranteed to return the same value for the same |
527 | // input. |
528 | StringPtr RenameImpl(const String& name, bool atomic); |
529 | |
530 | // Register an identity (name -> name) mapping in the renaming map. |
531 | // |
532 | // This essentially prevents the given name from being renamed. |
533 | void PreventRenaming(const String& name); |
534 | void PreventRenaming(const char* name); |
535 | |
536 | private: |
537 | // Build rename for the given |name|. |
538 | // |
539 | // For atomic renames BuildRename just returns the next |
540 | // available rename generated by AtomicRename(...). |
541 | // |
542 | // For non-atomic renames BuildRename ensures that private mangled |
543 | // identifiers (_ident@key) are renamed consistently with non-mangled |
544 | // counterparts (_ident). |
545 | StringPtr BuildRename(const String& name, bool atomic); |
546 | |
547 | // Generate a new rename. If |should_be_private| is set to true |
548 | // then we prefix returned identifier with '_'. |
549 | StringPtr NewAtomicRename(bool should_be_private); |
550 | |
551 | // Update next_ to generate the next free rename. |
552 | void NextName(); |
553 | |
554 | Thread* thread_; |
555 | |
556 | // Saved obfuscation state (ObjectStore::obfuscation_map()) |
557 | const Array& saved_state_; |
558 | |
559 | // Last used rename. Renames are only using a-zA-Z characters |
560 | // and are generated in order: a, b, ..., z, A, ..., Z, aa, ba, ... |
561 | char name_[100]; |
562 | |
563 | ObfuscationMap renames_; |
564 | |
565 | const String& private_key_; |
566 | |
567 | // Temporary handles. |
568 | String& string_; |
569 | String& renamed_; |
570 | }; |
571 | |
572 | // Current obfucation state or NULL if obfuscation is not enabled. |
573 | ObfuscationState* state_; |
574 | }; |
575 | #else |
576 | // Minimal do-nothing implementation of an Obfuscator for non-precompiler |
577 | // builds. |
578 | class Obfuscator { |
579 | public: |
580 | Obfuscator(Thread* thread, const String& private_key) {} |
581 | ~Obfuscator() {} |
582 | |
583 | StringPtr Rename(const String& name, bool atomic = false) { |
584 | return name.raw(); |
585 | } |
586 | |
587 | void PreventRenaming(const String& name) {} |
588 | |
589 | static void Deobfuscate(Thread* thread, const GrowableObjectArray& pieces) {} |
590 | }; |
591 | #endif // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32) |
592 | |
593 | } // namespace dart |
594 | |
595 | #endif // RUNTIME_VM_COMPILER_AOT_PRECOMPILER_H_ |
596 | |