OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/precompiler.h" | 5 #include "vm/precompiler.h" |
6 | 6 |
7 #include "vm/aot_optimizer.h" | 7 #include "vm/aot_optimizer.h" |
8 #include "vm/assembler.h" | 8 #include "vm/assembler.h" |
9 #include "vm/ast_printer.h" | 9 #include "vm/ast_printer.h" |
10 #include "vm/branch_optimizer.h" | 10 #include "vm/branch_optimizer.h" |
(...skipping 27 matching lines...) Expand all Loading... |
38 #include "vm/redundancy_elimination.h" | 38 #include "vm/redundancy_elimination.h" |
39 #include "vm/regexp_assembler.h" | 39 #include "vm/regexp_assembler.h" |
40 #include "vm/regexp_parser.h" | 40 #include "vm/regexp_parser.h" |
41 #include "vm/resolver.h" | 41 #include "vm/resolver.h" |
42 #include "vm/runtime_entry.h" | 42 #include "vm/runtime_entry.h" |
43 #include "vm/symbols.h" | 43 #include "vm/symbols.h" |
44 #include "vm/tags.h" | 44 #include "vm/tags.h" |
45 #include "vm/timeline.h" | 45 #include "vm/timeline.h" |
46 #include "vm/timer.h" | 46 #include "vm/timer.h" |
47 #include "vm/type_table.h" | 47 #include "vm/type_table.h" |
| 48 #include "vm/unicode.h" |
48 #include "vm/version.h" | 49 #include "vm/version.h" |
49 | 50 |
50 namespace dart { | 51 namespace dart { |
51 | 52 |
52 #define T (thread()) | 53 #define T (thread()) |
53 #define I (isolate()) | 54 #define I (isolate()) |
54 #define Z (zone()) | 55 #define Z (zone()) |
55 | 56 |
56 DEFINE_FLAG(bool, print_unique_targets, false, "Print unique dynamic targets"); | 57 DEFINE_FLAG(bool, print_unique_targets, false, "Print unique dynamic targets"); |
57 DEFINE_FLAG(bool, trace_precompiler, false, "Trace precompiler."); | 58 DEFINE_FLAG(bool, trace_precompiler, false, "Trace precompiler."); |
(...skipping 424 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
482 I->object_store()->set_async_star_move_next_helper(null_function); | 483 I->object_store()->set_async_star_move_next_helper(null_function); |
483 I->object_store()->set_complete_on_async_return(null_function); | 484 I->object_store()->set_complete_on_async_return(null_function); |
484 I->object_store()->set_async_star_stream_controller(null_class); | 485 I->object_store()->set_async_star_stream_controller(null_class); |
485 DropLibraryEntries(); | 486 DropLibraryEntries(); |
486 } | 487 } |
487 DropClasses(); | 488 DropClasses(); |
488 DropLibraries(); | 489 DropLibraries(); |
489 | 490 |
490 BindStaticCalls(); | 491 BindStaticCalls(); |
491 SwitchICCalls(); | 492 SwitchICCalls(); |
| 493 Obfuscate(); |
492 | 494 |
493 ProgramVisitor::Dedup(); | 495 ProgramVisitor::Dedup(); |
494 | 496 |
495 zone_ = NULL; | 497 zone_ = NULL; |
496 } | 498 } |
497 | 499 |
498 intptr_t symbols_before = -1; | 500 intptr_t symbols_before = -1; |
499 intptr_t symbols_after = -1; | 501 intptr_t symbols_after = -1; |
500 intptr_t capacity = -1; | 502 intptr_t capacity = -1; |
501 if (FLAG_trace_precompiler) { | 503 if (FLAG_trace_precompiler) { |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
603 current->field_->set_guarded_cid(cid); | 605 current->field_->set_guarded_cid(cid); |
604 current->field_->set_is_nullable(cid == kNullCid || cid == kDynamicCid); | 606 current->field_->set_is_nullable(cid == kNullCid || cid == kDynamicCid); |
605 if (FLAG_trace_precompiler) { | 607 if (FLAG_trace_precompiler) { |
606 THR_Print( | 608 THR_Print( |
607 "Field %s <- Type %s\n", current->field_->ToCString(), | 609 "Field %s <- Type %s\n", current->field_->ToCString(), |
608 Class::Handle(T->isolate()->class_table()->At(cid)).ToCString()); | 610 Class::Handle(T->isolate()->class_table()->At(cid)).ToCString()); |
609 } | 611 } |
610 } | 612 } |
611 } | 613 } |
612 | 614 |
613 void Precompiler::AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]) { | 615 static Dart_QualifiedFunctionName vm_entry_points[] = { |
614 // Note that <rootlibrary>.main is not a root. The appropriate main will be | |
615 // discovered through _getMainClosure. | |
616 | |
617 AddSelector(Symbols::NoSuchMethod()); | |
618 | |
619 AddSelector(Symbols::Call()); // For speed, not correctness. | |
620 | |
621 // Allocated from C++. | |
622 Class& cls = Class::Handle(Z); | |
623 for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) { | |
624 ASSERT(isolate()->class_table()->IsValidIndex(cid)); | |
625 if (!isolate()->class_table()->HasValidClassAt(cid)) { | |
626 continue; | |
627 } | |
628 if ((cid == kDynamicCid) || (cid == kVoidCid) || | |
629 (cid == kFreeListElement) || (cid == kForwardingCorpse)) { | |
630 continue; | |
631 } | |
632 cls = isolate()->class_table()->At(cid); | |
633 AddInstantiatedClass(cls); | |
634 } | |
635 | |
636 Dart_QualifiedFunctionName vm_entry_points[] = { | |
637 // Functions | 616 // Functions |
638 {"dart:core", "::", "_completeDeferredLoads"}, | 617 {"dart:core", "::", "_completeDeferredLoads"}, |
639 {"dart:core", "::", "identityHashCode"}, | 618 {"dart:core", "::", "identityHashCode"}, |
640 {"dart:core", "AbstractClassInstantiationError", | 619 {"dart:core", "AbstractClassInstantiationError", |
641 "AbstractClassInstantiationError._create"}, | 620 "AbstractClassInstantiationError._create"}, |
642 {"dart:core", "ArgumentError", "ArgumentError."}, | 621 {"dart:core", "ArgumentError", "ArgumentError."}, |
643 {"dart:core", "ArgumentError", "ArgumentError.value"}, | 622 {"dart:core", "ArgumentError", "ArgumentError.value"}, |
644 {"dart:core", "CyclicInitializationError", "CyclicInitializationError."}, | 623 {"dart:core", "CyclicInitializationError", "CyclicInitializationError."}, |
645 {"dart:core", "FallThroughError", "FallThroughError._create"}, | 624 {"dart:core", "FallThroughError", "FallThroughError._create"}, |
646 {"dart:core", "FormatException", "FormatException."}, | 625 {"dart:core", "FormatException", "FormatException."}, |
(...skipping 19 matching lines...) Expand all Loading... |
666 {"dart:typed_data", "_ByteBuffer", "_ByteBuffer._New"}, | 645 {"dart:typed_data", "_ByteBuffer", "_ByteBuffer._New"}, |
667 {"dart:_vmservice", "::", "boot"}, | 646 {"dart:_vmservice", "::", "boot"}, |
668 #if !defined(PRODUCT) | 647 #if !defined(PRODUCT) |
669 {"dart:_vmservice", "::", "_registerIsolate"}, | 648 {"dart:_vmservice", "::", "_registerIsolate"}, |
670 {"dart:developer", "Metrics", "_printMetrics"}, | 649 {"dart:developer", "Metrics", "_printMetrics"}, |
671 {"dart:developer", "::", "_runExtension"}, | 650 {"dart:developer", "::", "_runExtension"}, |
672 {"dart:isolate", "::", "_runPendingImmediateCallback"}, | 651 {"dart:isolate", "::", "_runPendingImmediateCallback"}, |
673 #endif // !PRODUCT | 652 #endif // !PRODUCT |
674 // Fields | 653 // Fields |
675 {"dart:core", "Error", "_stackTrace"}, | 654 {"dart:core", "Error", "_stackTrace"}, |
| 655 {"dart:core", "::", "_uriBaseClosure"}, |
676 {"dart:math", "_Random", "_state"}, | 656 {"dart:math", "_Random", "_state"}, |
677 {NULL, NULL, NULL} // Must be terminated with NULL entries. | 657 {NULL, NULL, NULL} // Must be terminated with NULL entries. |
678 }; | 658 }; |
| 659 |
| 660 void Precompiler::AddRoots(Dart_QualifiedFunctionName embedder_entry_points[]) { |
| 661 // Note that <rootlibrary>.main is not a root. The appropriate main will be |
| 662 // discovered through _getMainClosure. |
| 663 |
| 664 AddSelector(Symbols::NoSuchMethod()); |
| 665 |
| 666 AddSelector(Symbols::Call()); // For speed, not correctness. |
| 667 |
| 668 // Allocated from C++. |
| 669 Class& cls = Class::Handle(Z); |
| 670 for (intptr_t cid = kInstanceCid; cid < kNumPredefinedCids; cid++) { |
| 671 ASSERT(isolate()->class_table()->IsValidIndex(cid)); |
| 672 if (!isolate()->class_table()->HasValidClassAt(cid)) { |
| 673 continue; |
| 674 } |
| 675 if ((cid == kDynamicCid) || (cid == kVoidCid) || |
| 676 (cid == kFreeListElement) || (cid == kForwardingCorpse)) { |
| 677 continue; |
| 678 } |
| 679 cls = isolate()->class_table()->At(cid); |
| 680 AddInstantiatedClass(cls); |
| 681 } |
679 | 682 |
680 AddEntryPoints(vm_entry_points); | 683 AddEntryPoints(vm_entry_points); |
681 AddEntryPoints(embedder_entry_points); | 684 AddEntryPoints(embedder_entry_points); |
682 const Library& lib = Library::Handle(I->object_store()->root_library()); | 685 const Library& lib = Library::Handle(I->object_store()->root_library()); |
683 const String& name = String::Handle(String::New("main")); | 686 const String& name = String::Handle(String::New("main")); |
684 const Object& main_closure = Object::Handle(lib.GetFunctionClosure(name)); | 687 const Object& main_closure = Object::Handle(lib.GetFunctionClosure(name)); |
685 if (main_closure.IsClosure()) { | 688 if (main_closure.IsClosure()) { |
686 if (lib.LookupLocalFunction(name) == Function::null()) { | 689 if (lib.LookupLocalFunction(name) == Function::null()) { |
687 // Check whether the function is in exported namespace of library, in | 690 // Check whether the function is in exported namespace of library, in |
688 // this case we have to retain the root library caches. | 691 // this case we have to retain the root library caches. |
(...skipping 1582 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2271 Code& target_code_; | 2274 Code& target_code_; |
2272 UnlinkedCallSet canonical_unlinked_calls_; | 2275 UnlinkedCallSet canonical_unlinked_calls_; |
2273 }; | 2276 }; |
2274 | 2277 |
2275 ASSERT(!I->compilation_allowed()); | 2278 ASSERT(!I->compilation_allowed()); |
2276 SwitchICCallsVisitor visitor(Z); | 2279 SwitchICCallsVisitor visitor(Z); |
2277 ProgramVisitor::VisitFunctions(&visitor); | 2280 ProgramVisitor::VisitFunctions(&visitor); |
2278 #endif | 2281 #endif |
2279 } | 2282 } |
2280 | 2283 |
| 2284 void Precompiler::Obfuscate() { |
| 2285 if (!I->obfuscate()) { |
| 2286 return; |
| 2287 } |
| 2288 |
| 2289 class ScriptsCollector : public ObjectVisitor { |
| 2290 public: |
| 2291 explicit ScriptsCollector(Zone* zone, |
| 2292 GrowableHandlePtrArray<const Script>* scripts) |
| 2293 : script_(Script::Handle(zone)), scripts_(scripts) {} |
| 2294 |
| 2295 void VisitObject(RawObject* obj) { |
| 2296 if (obj->GetClassId() == kScriptCid) { |
| 2297 script_ ^= obj; |
| 2298 scripts_->Add(Script::Cast(script_)); |
| 2299 } |
| 2300 } |
| 2301 |
| 2302 private: |
| 2303 Script& script_; |
| 2304 GrowableHandlePtrArray<const Script>* scripts_; |
| 2305 }; |
| 2306 |
| 2307 GrowableHandlePtrArray<const Script> scripts(Z, 100); |
| 2308 Isolate::Current()->heap()->CollectAllGarbage(); |
| 2309 { |
| 2310 HeapIterationScope his(T); |
| 2311 ScriptsCollector visitor(Z, &scripts); |
| 2312 I->heap()->VisitObjects(&visitor); |
| 2313 } |
| 2314 |
| 2315 { |
| 2316 // Note: when this object is destroyed it will commit obfuscation |
| 2317 // mappings into the ObjectStore. Hence the block around it - to |
| 2318 // ensure that destructor is called before we save obfuscation |
| 2319 // mappings and clear the ObjectStore. |
| 2320 Obfuscator obfuscator(T, /*private_key=*/String::Handle(Z)); |
| 2321 String& str = String::Handle(Z); |
| 2322 for (intptr_t i = 0; i < scripts.length(); i++) { |
| 2323 const Script& script = scripts.At(i); |
| 2324 |
| 2325 str = script.url(); |
| 2326 str = Symbols::New(T, str); |
| 2327 str = obfuscator.Rename(str, /*atomic=*/true); |
| 2328 script.set_url(str); |
| 2329 |
| 2330 str = script.resolved_url(); |
| 2331 str = Symbols::New(T, str); |
| 2332 str = obfuscator.Rename(str, /*atomic=*/true); |
| 2333 script.set_resolved_url(str); |
| 2334 } |
| 2335 |
| 2336 Library& lib = Library::Handle(); |
| 2337 for (intptr_t i = 0; i < libraries_.Length(); i++) { |
| 2338 lib ^= libraries_.At(i); |
| 2339 if (!lib.is_dart_scheme()) { |
| 2340 str = lib.name(); |
| 2341 str = obfuscator.Rename(str, /*atomic=*/true); |
| 2342 lib.set_name(str); |
| 2343 |
| 2344 str = lib.url(); |
| 2345 str = Symbols::New(T, str); |
| 2346 str = obfuscator.Rename(str, /*atomic=*/true); |
| 2347 lib.set_url(str); |
| 2348 } |
| 2349 } |
| 2350 Library::RegisterLibraries(T, libraries_); |
| 2351 } |
| 2352 |
| 2353 // Obfuscation is done. Move obfuscation map into malloced memory. |
| 2354 I->set_obfuscation_map(Obfuscator::SerializeMap(T)); |
| 2355 |
| 2356 // Discard obfuscation mappings to avoid including them into snapshot. |
| 2357 I->object_store()->set_obfuscation_map(Array::Handle(Z)); |
| 2358 } |
| 2359 |
2281 void Precompiler::FinalizeAllClasses() { | 2360 void Precompiler::FinalizeAllClasses() { |
2282 Library& lib = Library::Handle(Z); | 2361 Library& lib = Library::Handle(Z); |
2283 Class& cls = Class::Handle(Z); | 2362 Class& cls = Class::Handle(Z); |
2284 | 2363 |
2285 for (intptr_t i = 0; i < libraries_.Length(); i++) { | 2364 for (intptr_t i = 0; i < libraries_.Length(); i++) { |
2286 lib ^= libraries_.At(i); | 2365 lib ^= libraries_.At(i); |
2287 if (!lib.Loaded()) { | 2366 if (!lib.Loaded()) { |
2288 String& uri = String::Handle(Z, lib.url()); | 2367 String& uri = String::Handle(Z, lib.url()); |
2289 String& msg = String::Handle( | 2368 String& msg = String::Handle( |
2290 Z, | 2369 Z, |
(...skipping 1032 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3323 FieldTypeMap* field_type_map) { | 3402 FieldTypeMap* field_type_map) { |
3324 VMTagScope tagScope(thread, VMTag::kCompileUnoptimizedTagId); | 3403 VMTagScope tagScope(thread, VMTag::kCompileUnoptimizedTagId); |
3325 TIMELINE_FUNCTION_COMPILATION_DURATION(thread, "CompileFunction", function); | 3404 TIMELINE_FUNCTION_COMPILATION_DURATION(thread, "CompileFunction", function); |
3326 | 3405 |
3327 ASSERT(FLAG_precompiled_mode); | 3406 ASSERT(FLAG_precompiled_mode); |
3328 const bool optimized = function.IsOptimizable(); // False for natives. | 3407 const bool optimized = function.IsOptimizable(); // False for natives. |
3329 DartPrecompilationPipeline pipeline(zone, field_type_map); | 3408 DartPrecompilationPipeline pipeline(zone, field_type_map); |
3330 return PrecompileFunctionHelper(precompiler, &pipeline, function, optimized); | 3409 return PrecompileFunctionHelper(precompiler, &pipeline, function, optimized); |
3331 } | 3410 } |
3332 | 3411 |
| 3412 Obfuscator::Obfuscator(Thread* thread, const String& private_key) |
| 3413 : state_(NULL) { |
| 3414 Isolate* isolate = thread->isolate(); |
| 3415 Zone* zone = thread->zone(); |
| 3416 if (!isolate->obfuscate()) { |
| 3417 // Nothing to do. |
| 3418 return; |
| 3419 } |
| 3420 |
| 3421 // Create ObfuscationState from ObjectStore::obfusction_map(). |
| 3422 ObjectStore* store = thread->isolate()->object_store(); |
| 3423 Array& obfuscation_state = Array::Handle(zone, store->obfuscation_map()); |
| 3424 |
| 3425 if (store->obfuscation_map() == Array::null()) { |
| 3426 // We are just starting the obfuscation. Create initial state. |
| 3427 const int kInitialPrivateCapacity = 256; |
| 3428 obfuscation_state = Array::New(kSavedStateSize); |
| 3429 obfuscation_state.SetAt( |
| 3430 1, Array::Handle(zone, HashTables::New<ObfuscationMap>( |
| 3431 kInitialPrivateCapacity, Heap::kOld))); |
| 3432 } |
| 3433 |
| 3434 state_ = new (zone) ObfuscationState(thread, obfuscation_state, private_key); |
| 3435 |
| 3436 if (store->obfuscation_map() == Array::null()) { |
| 3437 // We are just starting the obfuscation. Initialize the renaming map. |
| 3438 // Note: InitializeRenamingMap uses state_. |
| 3439 InitializeRenamingMap(isolate); |
| 3440 } |
| 3441 } |
| 3442 |
| 3443 Obfuscator::~Obfuscator() { |
| 3444 if (state_ != NULL) { |
| 3445 state_->SaveState(); |
| 3446 } |
| 3447 } |
| 3448 |
| 3449 void Obfuscator::InitializeRenamingMap(Isolate* isolate) { |
| 3450 // Prevent renaming of classes and method names mentioned in the |
| 3451 // entry points lists. |
| 3452 PreventRenaming(vm_entry_points); |
| 3453 PreventRenaming(isolate->embedder_entry_points()); |
| 3454 |
| 3455 // Prevent renaming of all pseudo-keywords and operators. |
| 3456 // Note: not all pseudo-keywords are mentioned in DART_KEYWORD_LIST |
| 3457 // (for example 'hide', 'show' and async related keywords are omitted). |
| 3458 // Those are protected from renaming as part of all symbols. |
| 3459 #define PREVENT_RENAMING(name, value, priority, attr) \ |
| 3460 do { \ |
| 3461 if (Token::CanBeOverloaded(Token::name) || \ |
| 3462 ((Token::attr & Token::kPseudoKeyword) != 0)) { \ |
| 3463 PreventRenaming(value); \ |
| 3464 } \ |
| 3465 } while (0); |
| 3466 |
| 3467 DART_TOKEN_LIST(PREVENT_RENAMING) |
| 3468 DART_KEYWORD_LIST(PREVENT_RENAMING) |
| 3469 #undef PREVENT_RENAMING |
| 3470 |
| 3471 // this is a keyword token unless it occurs in the string interpolation |
| 3472 // which causes it to be obfuscated. |
| 3473 PreventRenaming("this"); |
| 3474 |
| 3475 // Protect all symbols from renaming. |
| 3476 #define PREVENT_RENAMING(name, value) PreventRenaming(value); |
| 3477 PREDEFINED_SYMBOLS_LIST(PREVENT_RENAMING) |
| 3478 #undef PREVENT_RENAMING |
| 3479 |
| 3480 // Protect NativeFieldWrapperClassX names from being obfuscated. Those |
| 3481 // classes are created manually by the runtime system. |
| 3482 // TODO(dartbug.com/30524) instead call to Obfuscator::Rename from a place |
| 3483 // where these are created. |
| 3484 PreventRenaming("NativeFieldWrapperClass1"); |
| 3485 PreventRenaming("NativeFieldWrapperClass2"); |
| 3486 PreventRenaming("NativeFieldWrapperClass3"); |
| 3487 PreventRenaming("NativeFieldWrapperClass4"); |
| 3488 |
| 3489 // Prevent renaming of ClassID.cid* fields. These fields are injected by |
| 3490 // runtime. |
| 3491 // TODO(dartbug.com/30524) instead call to Obfuscator::Rename from a place |
| 3492 // where these are created. |
| 3493 #define CLASS_LIST_WITH_NULL(V) \ |
| 3494 V(Null) \ |
| 3495 CLASS_LIST_NO_OBJECT(V) |
| 3496 #define PREVENT_RENAMING(clazz) PreventRenaming("cid" #clazz); |
| 3497 CLASS_LIST_WITH_NULL(PREVENT_RENAMING) |
| 3498 #undef PREVENT_RENAMING |
| 3499 #undef CLASS_LIST_WITH_NULL |
| 3500 |
| 3501 // Prevent renaming of methods that are looked up by method recognizer. |
| 3502 // TODO(dartbug.com/30524) instead call to Obfuscator::Rename from a place |
| 3503 // where these are looked up. |
| 3504 #define PREVENT_RENAMING(class_name, function_name, recognized_enum, \ |
| 3505 result_type, fingerprint) \ |
| 3506 do { \ |
| 3507 PreventRenaming(#class_name); \ |
| 3508 PreventRenaming(#function_name); \ |
| 3509 } while (0); |
| 3510 RECOGNIZED_LIST(PREVENT_RENAMING) |
| 3511 #undef PREVENT_RENAMING |
| 3512 |
| 3513 // Prevent renaming of methods that are looked up by method recognizer. |
| 3514 // TODO(dartbug.com/30524) instead call to Obfuscator::Rename from a place |
| 3515 // where these are looked up. |
| 3516 #define PREVENT_RENAMING(class_name, function_name, recognized_enum, \ |
| 3517 fingerprint) \ |
| 3518 do { \ |
| 3519 PreventRenaming(#class_name); \ |
| 3520 PreventRenaming(#function_name); \ |
| 3521 } while (0); |
| 3522 INLINE_WHITE_LIST(PREVENT_RENAMING) |
| 3523 INLINE_BLACK_LIST(PREVENT_RENAMING) |
| 3524 POLYMORPHIC_TARGET_LIST(PREVENT_RENAMING) |
| 3525 #undef PREVENT_RENAMING |
| 3526 |
| 3527 // These are not mentioned by entry points but are still looked up by name. |
| 3528 // (They are not mentioned in the entry points because we don't need them |
| 3529 // after the compilation) |
| 3530 PreventRenaming("_resolveScriptUri"); |
| 3531 |
| 3532 // Precompiler is looking up "main". |
| 3533 // TODO(dartbug.com/30524) instead call to Obfuscator::Rename from a place |
| 3534 // where these are created. |
| 3535 PreventRenaming("main"); |
| 3536 |
| 3537 // Fast path for common conditional import. See Deobfuscate method. |
| 3538 PreventRenaming("dart"); |
| 3539 PreventRenaming("library"); |
| 3540 PreventRenaming("io"); |
| 3541 PreventRenaming("html"); |
| 3542 } |
| 3543 |
| 3544 RawString* Obfuscator::ObfuscationState::RenameImpl(const String& name, |
| 3545 bool atomic) { |
| 3546 ASSERT(name.IsSymbol()); |
| 3547 |
| 3548 renamed_ ^= renames_.GetOrNull(name); |
| 3549 if (renamed_.IsNull()) { |
| 3550 renamed_ = BuildRename(name, atomic); |
| 3551 renames_.UpdateOrInsert(name, renamed_); |
| 3552 } |
| 3553 return renamed_.raw(); |
| 3554 } |
| 3555 |
| 3556 void Obfuscator::PreventRenaming(Dart_QualifiedFunctionName entry_points[]) { |
| 3557 for (intptr_t i = 0; entry_points[i].function_name != NULL; i++) { |
| 3558 const char* class_name = entry_points[i].class_name; |
| 3559 const char* function_name = entry_points[i].function_name; |
| 3560 |
| 3561 const size_t class_name_len = strlen(class_name); |
| 3562 if (strncmp(function_name, class_name, class_name_len) == 0 && |
| 3563 function_name[class_name_len] == '.') { |
| 3564 const char* ctor_name = function_name + class_name_len + 1; |
| 3565 if (ctor_name[0] != '\0') { |
| 3566 PreventRenaming(ctor_name); |
| 3567 } |
| 3568 } else { |
| 3569 PreventRenaming(function_name); |
| 3570 } |
| 3571 PreventRenaming(class_name); |
| 3572 } |
| 3573 } |
| 3574 |
| 3575 static const char* const kGetterPrefix = "get:"; |
| 3576 static const intptr_t kGetterPrefixLength = strlen(kGetterPrefix); |
| 3577 static const char* const kSetterPrefix = "set:"; |
| 3578 static const intptr_t kSetterPrefixLength = strlen(kSetterPrefix); |
| 3579 |
| 3580 void Obfuscator::PreventRenaming(const char* name) { |
| 3581 // For constructor names Class.name skip class name (if any) and a dot. |
| 3582 const char* dot = strchr(name, '.'); |
| 3583 if (dot != NULL) { |
| 3584 name = dot + 1; |
| 3585 } |
| 3586 |
| 3587 // Empty name: do nothing. |
| 3588 if (name[0] == '\0') { |
| 3589 return; |
| 3590 } |
| 3591 |
| 3592 // Skip get: and set: prefixes. |
| 3593 if (strncmp(name, kGetterPrefix, kGetterPrefixLength) == 0) { |
| 3594 name = name + kGetterPrefixLength; |
| 3595 } else if (strncmp(name, kSetterPrefix, kSetterPrefixLength) == 0) { |
| 3596 name = name + kSetterPrefixLength; |
| 3597 } |
| 3598 |
| 3599 state_->PreventRenaming(name); |
| 3600 } |
| 3601 |
| 3602 void Obfuscator::ObfuscationState::SaveState() { |
| 3603 saved_state_.SetAt(kSavedStateNameIndex, String::Handle(String::New(name_))); |
| 3604 saved_state_.SetAt(kSavedStateRenamesIndex, renames_.Release()); |
| 3605 thread_->isolate()->object_store()->set_obfuscation_map(saved_state_); |
| 3606 } |
| 3607 |
| 3608 void Obfuscator::ObfuscationState::PreventRenaming(const char* name) { |
| 3609 string_ = Symbols::New(thread_, name); |
| 3610 PreventRenaming(string_); |
| 3611 } |
| 3612 |
| 3613 void Obfuscator::ObfuscationState::PreventRenaming(const String& name) { |
| 3614 renames_.UpdateOrInsert(name, name); |
| 3615 } |
| 3616 |
| 3617 void Obfuscator::ObfuscationState::NextName() { |
| 3618 // We apply the following rules: |
| 3619 // |
| 3620 // inc(a) = b, ... , inc(z) = A, ..., inc(Z) = a & carry. |
| 3621 // |
| 3622 for (intptr_t i = 0;; i++) { |
| 3623 const char digit = name_[i]; |
| 3624 if (digit == '\0') { |
| 3625 name_[i] = 'a'; |
| 3626 } else if (digit < 'Z') { |
| 3627 name_[i]++; |
| 3628 } else if (digit == 'Z') { |
| 3629 name_[i] = 'a'; |
| 3630 continue; // Carry. |
| 3631 } else if (digit < 'z') { |
| 3632 name_[i]++; |
| 3633 } else { |
| 3634 name_[i] = 'A'; |
| 3635 } |
| 3636 break; |
| 3637 } |
| 3638 } |
| 3639 |
| 3640 RawString* Obfuscator::ObfuscationState::NewAtomicRename( |
| 3641 bool should_be_private) { |
| 3642 do { |
| 3643 NextName(); |
| 3644 renamed_ = Symbols::NewFormatted(thread_, "%s%s", |
| 3645 should_be_private ? "_" : "", name_); |
| 3646 // Must check if our generated name clashes with something that will |
| 3647 // have an identity renaming. |
| 3648 } while (renames_.GetOrNull(renamed_) == renamed_.raw()); |
| 3649 return renamed_.raw(); |
| 3650 } |
| 3651 |
| 3652 RawString* Obfuscator::ObfuscationState::BuildRename(const String& name, |
| 3653 bool atomic) { |
| 3654 const bool is_private = name.CharAt(0) == '_'; |
| 3655 if (!atomic && is_private) { |
| 3656 // Find the first '@'. |
| 3657 intptr_t i = 0; |
| 3658 while (i < name.Length() && name.CharAt(i) != '@') { |
| 3659 i++; |
| 3660 } |
| 3661 const intptr_t end = i; |
| 3662 |
| 3663 // Follow the rule: |
| 3664 // |
| 3665 // Rename(_ident@key) = Rename(_ident)@private_key_. |
| 3666 // |
| 3667 string_ = Symbols::New(thread_, name, 0, end); |
| 3668 string_ = RenameImpl(string_, /*atomic=*/true); |
| 3669 return Symbols::FromConcat(thread_, string_, private_key_); |
| 3670 } else { |
| 3671 return NewAtomicRename(is_private); |
| 3672 } |
| 3673 } |
| 3674 |
| 3675 void Obfuscator::ObfuscateSymbolInstance(Thread* thread, |
| 3676 const Instance& symbol) { |
| 3677 // Note: this must match dart:internal.Symbol declaration. |
| 3678 const intptr_t kSymbolNameOffset = kWordSize; |
| 3679 |
| 3680 Object& name_value = String::Handle(); |
| 3681 name_value = symbol.RawGetFieldAtOffset(kSymbolNameOffset); |
| 3682 if (!name_value.IsString()) { |
| 3683 // dart:internal.Symbol constructor does not validate its input. |
| 3684 return; |
| 3685 } |
| 3686 |
| 3687 String& name = String::Handle(); |
| 3688 name ^= name_value.raw(); |
| 3689 |
| 3690 // TODO(vegorov) it is quite wasteful to create an obfuscator per-symbol. |
| 3691 Obfuscator obfuscator(thread, /*private_key=*/String::Handle()); |
| 3692 |
| 3693 // Symbol can be a sequence of identifiers separated by dots. |
| 3694 // We split such symbols into components and obfuscate individual identifiers |
| 3695 // separately. |
| 3696 String& component = String::Handle(); |
| 3697 GrowableHandlePtrArray<const String> renamed(thread->zone(), 2); |
| 3698 |
| 3699 const intptr_t length = name.Length(); |
| 3700 intptr_t i = 0, start = 0; |
| 3701 while (i < length) { |
| 3702 // First look for a '.' in the symbol. |
| 3703 start = i; |
| 3704 while (i < length && name.CharAt(i) != '.') { |
| 3705 i++; |
| 3706 } |
| 3707 const intptr_t end = i; |
| 3708 if (end == length) { |
| 3709 break; |
| 3710 } |
| 3711 |
| 3712 if (start != end) { |
| 3713 component = Symbols::New(thread, name, start, end - start); |
| 3714 component = obfuscator.Rename(component, /*atomic=*/true); |
| 3715 renamed.Add(component); |
| 3716 } |
| 3717 |
| 3718 renamed.Add(Symbols::Dot()); |
| 3719 i++; // Skip '.' |
| 3720 } |
| 3721 |
| 3722 // Handle the last component [start, length). |
| 3723 // If symbol ends up at = and it is not one of '[]=', '==', '<=' or |
| 3724 // '>=' then we treat it as a setter symbol and follow the rule: |
| 3725 // |
| 3726 // Rename('ident=') = Rename('ident') '=' |
| 3727 // |
| 3728 const bool is_setter = (length - start) > 1 && |
| 3729 name.CharAt(length - 1) == '=' && |
| 3730 !(name.Equals(Symbols::AssignIndexToken()) || |
| 3731 name.Equals(Symbols::EqualOperator()) || |
| 3732 name.Equals(Symbols::GreaterEqualOperator()) || |
| 3733 name.Equals(Symbols::LessEqualOperator())); |
| 3734 const intptr_t end = length - (is_setter ? 1 : 0); |
| 3735 |
| 3736 if ((start == 0) && (end == length) && name.IsSymbol()) { |
| 3737 component = name.raw(); |
| 3738 } else { |
| 3739 component = Symbols::New(thread, name, start, end - start); |
| 3740 } |
| 3741 component = obfuscator.Rename(component, /*atomic=*/true); |
| 3742 renamed.Add(component); |
| 3743 |
| 3744 if (is_setter) { |
| 3745 renamed.Add(Symbols::Equals()); |
| 3746 } |
| 3747 |
| 3748 name = Symbols::FromConcatAll(thread, renamed); |
| 3749 symbol.RawSetFieldAtOffset(kSymbolNameOffset, name); |
| 3750 } |
| 3751 |
| 3752 void Obfuscator::Deobfuscate(Thread* thread, |
| 3753 const GrowableObjectArray& pieces) { |
| 3754 const Array& obfuscation_state = Array::Handle( |
| 3755 thread->zone(), thread->isolate()->object_store()->obfuscation_map()); |
| 3756 if (obfuscation_state.IsNull()) { |
| 3757 return; |
| 3758 } |
| 3759 |
| 3760 const Array& renames = Array::Handle( |
| 3761 thread->zone(), GetRenamesFromSavedState(obfuscation_state)); |
| 3762 |
| 3763 ObfuscationMap renames_map(renames.raw()); |
| 3764 String& piece = String::Handle(); |
| 3765 for (intptr_t i = 0; i < pieces.Length(); i++) { |
| 3766 piece ^= pieces.At(i); |
| 3767 ASSERT(piece.IsSymbol()); |
| 3768 |
| 3769 // Fast path: skip '.' |
| 3770 if (piece.raw() == Symbols::Dot().raw()) { |
| 3771 continue; |
| 3772 } |
| 3773 |
| 3774 // Fast path: check if piece has an identity obfuscation. |
| 3775 if (renames_map.GetOrNull(piece) == piece.raw()) { |
| 3776 continue; |
| 3777 } |
| 3778 |
| 3779 // Search through the whole obfuscation map until matching value is found. |
| 3780 // We are using linear search instead of generating a reverse mapping |
| 3781 // because we assume that Deobfuscate() method is almost never called. |
| 3782 ObfuscationMap::Iterator it(&renames_map); |
| 3783 while (it.MoveNext()) { |
| 3784 const intptr_t entry = it.Current(); |
| 3785 if (renames_map.GetPayload(entry, 0) == piece.raw()) { |
| 3786 piece ^= renames_map.GetKey(entry); |
| 3787 pieces.SetAt(i, piece); |
| 3788 break; |
| 3789 } |
| 3790 } |
| 3791 } |
| 3792 renames_map.Release(); |
| 3793 } |
| 3794 |
| 3795 static const char* StringToCString(const String& str) { |
| 3796 const intptr_t len = Utf8::Length(str); |
| 3797 char* result = new char[len + 1]; |
| 3798 str.ToUTF8(reinterpret_cast<uint8_t*>(result), len); |
| 3799 result[len] = 0; |
| 3800 return result; |
| 3801 } |
| 3802 |
| 3803 const char** Obfuscator::SerializeMap(Thread* thread) { |
| 3804 const Array& obfuscation_state = Array::Handle( |
| 3805 thread->zone(), thread->isolate()->object_store()->obfuscation_map()); |
| 3806 if (obfuscation_state.IsNull()) { |
| 3807 return NULL; |
| 3808 } |
| 3809 |
| 3810 const Array& renames = Array::Handle( |
| 3811 thread->zone(), GetRenamesFromSavedState(obfuscation_state)); |
| 3812 ObfuscationMap renames_map(renames.raw()); |
| 3813 |
| 3814 const char** result = new const char*[renames_map.NumOccupied() * 2 + 1]; |
| 3815 intptr_t idx = 0; |
| 3816 String& str = String::Handle(); |
| 3817 |
| 3818 ObfuscationMap::Iterator it(&renames_map); |
| 3819 while (it.MoveNext()) { |
| 3820 const intptr_t entry = it.Current(); |
| 3821 str ^= renames_map.GetKey(entry); |
| 3822 result[idx++] = StringToCString(str); |
| 3823 str ^= renames_map.GetPayload(entry, 0); |
| 3824 result[idx++] = StringToCString(str); |
| 3825 } |
| 3826 result[idx++] = NULL; |
| 3827 renames_map.Release(); |
| 3828 |
| 3829 return result; |
| 3830 } |
| 3831 |
3333 #endif // DART_PRECOMPILER | 3832 #endif // DART_PRECOMPILER |
3334 | 3833 |
3335 } // namespace dart | 3834 } // namespace dart |
OLD | NEW |