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
20namespace dart {
21
22// Forward declarations.
23class Class;
24class Error;
25class Field;
26class Function;
27class GrowableObjectArray;
28class String;
29class Precompiler;
30class FlowGraph;
31class PrecompilerTracer;
32
33class 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
49typedef DirectChainedHashMap<TableSelectorKeyValueTrait> TableSelectorSet;
50
51class 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
69typedef DirectChainedHashMap<SymbolKeyValueTrait> SymbolSet;
70
71// Traits for the HashTable template.
72struct 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
81typedef UnorderedHashSet<FunctionKeyTraits> FunctionSet;
82
83class 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
107typedef DirectChainedHashMap<FieldKeyValueTrait> FieldSet;
108
109class 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
127typedef DirectChainedHashMap<ClassKeyValueTrait> ClassSet;
128
129class 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
147typedef DirectChainedHashMap<AbstractTypeKeyValueTrait> AbstractTypeSet;
148
149class 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
167typedef DirectChainedHashMap<TypeParameterKeyValueTrait> TypeParameterSet;
168
169class 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
187typedef DirectChainedHashMap<TypeArgumentsKeyValueTrait> TypeArgumentsSet;
188
189class 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
207typedef DirectChainedHashMap<InstanceKeyValueTrait> InstanceSet;
208
209class 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
378class 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
389typedef UnorderedHashMap<FunctionsTraits> UniqueFunctionsMap;
390
391#if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
392// ObfuscationMap maps Strings to Strings.
393class 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};
405typedef 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//
410class 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.
578class 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