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