|  | // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | #include "vm/unit_test.h" | 
|  |  | 
|  | #include <stdio.h> | 
|  |  | 
|  | #include "bin/builtin.h" | 
|  | #include "bin/dartutils.h" | 
|  | #include "bin/isolate_data.h" | 
|  | #include "bin/uri.h" | 
|  |  | 
|  | #include "platform/globals.h" | 
|  |  | 
|  | #include "vm/compiler/assembler/assembler.h" | 
|  | #include "vm/compiler/assembler/disassembler.h" | 
|  | #include "vm/compiler/jit/compiler.h" | 
|  | #include "vm/dart_api_impl.h" | 
|  | #include "vm/isolate_reload.h" | 
|  | #include "vm/kernel_isolate.h" | 
|  | #include "vm/parser.h" | 
|  | #include "vm/symbols.h" | 
|  | #include "vm/thread.h" | 
|  | #include "vm/virtual_memory.h" | 
|  |  | 
|  | using dart::bin::Builtin; | 
|  | using dart::bin::DartUtils; | 
|  |  | 
|  | extern "C" { | 
|  | extern const uint8_t kPlatformDill[]; | 
|  | extern intptr_t kPlatformDillSize; | 
|  | } | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | DECLARE_FLAG(bool, gc_during_reload); | 
|  | DECLARE_FLAG(bool, force_evacuation); | 
|  |  | 
|  | const uint8_t* platform_dill = kPlatformDill; | 
|  | const intptr_t platform_dill_size = kPlatformDillSize; | 
|  |  | 
|  | const uint8_t* TesterState::vm_snapshot_data = nullptr; | 
|  | Dart_IsolateGroupCreateCallback TesterState::create_callback = nullptr; | 
|  | Dart_IsolateShutdownCallback TesterState::shutdown_callback = nullptr; | 
|  | Dart_IsolateGroupCleanupCallback TesterState::group_cleanup_callback = nullptr; | 
|  | const char** TesterState::argv = nullptr; | 
|  | int TesterState::argc = 0; | 
|  |  | 
|  | void KernelBufferList::AddBufferToList(const uint8_t* kernel_buffer) { | 
|  | next_ = new KernelBufferList(kernel_buffer_, next_); | 
|  | kernel_buffer_ = kernel_buffer; | 
|  | } | 
|  |  | 
|  | TestCaseBase* TestCaseBase::first_ = nullptr; | 
|  | TestCaseBase* TestCaseBase::tail_ = nullptr; | 
|  | KernelBufferList* TestCaseBase::current_kernel_buffers_ = nullptr; | 
|  |  | 
|  | TestCaseBase::TestCaseBase(const char* name, const char* expectation) | 
|  | : raw_test_(false), | 
|  | next_(nullptr), | 
|  | name_(name), | 
|  | expectation_(strlen(expectation) > 0 ? expectation : "Pass") { | 
|  | if (first_ == nullptr) { | 
|  | first_ = this; | 
|  | } else { | 
|  | tail_->next_ = this; | 
|  | } | 
|  | tail_ = this; | 
|  | } | 
|  |  | 
|  | void TestCaseBase::RunAllRaw() { | 
|  | TestCaseBase* test = first_; | 
|  | while (test != nullptr) { | 
|  | if (test->raw_test_) { | 
|  | test->RunTest(); | 
|  | CleanupState(); | 
|  | } | 
|  | test = test->next_; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TestCaseBase::RunAll() { | 
|  | TestCaseBase* test = first_; | 
|  | while (test != nullptr) { | 
|  | if (!test->raw_test_) { | 
|  | test->RunTest(); | 
|  | CleanupState(); | 
|  | } | 
|  | test = test->next_; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TestCaseBase::CleanupState() { | 
|  | if (current_kernel_buffers_ != nullptr) { | 
|  | delete current_kernel_buffers_; | 
|  | current_kernel_buffers_ = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TestCaseBase::AddToKernelBuffers(const uint8_t* kernel_buffer) { | 
|  | ASSERT(kernel_buffer != nullptr); | 
|  | if (current_kernel_buffers_ == nullptr) { | 
|  | current_kernel_buffers_ = new KernelBufferList(kernel_buffer); | 
|  | } else { | 
|  | current_kernel_buffers_->AddBufferToList(kernel_buffer); | 
|  | } | 
|  | } | 
|  |  | 
|  | Dart_Isolate TestCase::CreateIsolate(const uint8_t* data_buffer, | 
|  | intptr_t len, | 
|  | const uint8_t* instr_buffer, | 
|  | const char* name, | 
|  | void* group_data, | 
|  | void* isolate_data) { | 
|  | char* err; | 
|  | Dart_IsolateFlags api_flags; | 
|  | Isolate::FlagsInitialize(&api_flags); | 
|  | api_flags.null_safety = true; | 
|  | Dart_Isolate isolate = nullptr; | 
|  | if (len == 0) { | 
|  | isolate = Dart_CreateIsolateGroup( | 
|  | /*script_uri=*/name, /*name=*/name, data_buffer, instr_buffer, | 
|  | &api_flags, group_data, isolate_data, &err); | 
|  | } else { | 
|  | isolate = Dart_CreateIsolateGroupFromKernel(/*script_uri=*/name, | 
|  | /*name=*/name, data_buffer, len, | 
|  | &api_flags, group_data, | 
|  | isolate_data, &err); | 
|  | } | 
|  | if (isolate == nullptr) { | 
|  | OS::PrintErr("Creation of isolate failed '%s'\n", err); | 
|  | free(err); | 
|  | } | 
|  |  | 
|  | EXPECT(isolate != nullptr); | 
|  | return isolate; | 
|  | } | 
|  |  | 
|  | Dart_Isolate TestCase::CreateTestIsolate(const char* name, | 
|  | void* group_data, | 
|  | void* isolate_data) { | 
|  | return CreateIsolate(bin::core_isolate_snapshot_data, | 
|  | 0 /* Snapshots have length encoded within them. */, | 
|  | bin::core_isolate_snapshot_instructions, name, | 
|  | group_data, isolate_data); | 
|  | } | 
|  |  | 
|  | void SetupCoreLibrariesForUnitTest() { | 
|  | TransitionVMToNative transition(Thread::Current()); | 
|  |  | 
|  | Dart_EnterScope(); | 
|  | bool ok = bin::DartUtils::SetOriginalWorkingDirectory(); | 
|  | RELEASE_ASSERT(ok); | 
|  | Dart_Handle result = bin::DartUtils::PrepareForScriptLoading( | 
|  | /*is_service_isolate=*/false, | 
|  | /*trace_loading=*/false, /*flag_profile_microtasks=*/false); | 
|  | Dart_ExitScope(); | 
|  |  | 
|  | RELEASE_ASSERT(!Dart_IsError(result)); | 
|  | } | 
|  |  | 
|  | Dart_Isolate TestCase::CreateTestIsolateInGroup(const char* name, | 
|  | Dart_Isolate parent, | 
|  | void* group_data, | 
|  | void* isolate_data) { | 
|  | char* error; | 
|  | Isolate* result = CreateWithinExistingIsolateGroup( | 
|  | reinterpret_cast<Isolate*>(parent)->group(), name, &error); | 
|  | if (error != nullptr) { | 
|  | OS::PrintErr("CreateTestIsolateInGroup failed: %s\n", error); | 
|  | free(error); | 
|  | } | 
|  | EXPECT(result != nullptr); | 
|  | return Api::CastIsolate(result); | 
|  | } | 
|  |  | 
|  | struct TestLibEntry { | 
|  | const char* url; | 
|  | const char* source; | 
|  | }; | 
|  |  | 
|  | static MallocGrowableArray<TestLibEntry>* test_libs_ = nullptr; | 
|  |  | 
|  | const char* TestCase::url() { | 
|  | return RESOLVED_USER_TEST_URI; | 
|  | } | 
|  |  | 
|  | void TestCase::AddTestLib(const char* url, const char* source) { | 
|  | if (test_libs_ == nullptr) { | 
|  | test_libs_ = new MallocGrowableArray<TestLibEntry>(); | 
|  | } | 
|  | // If the test lib is already added, replace the source. | 
|  | for (intptr_t i = 0; i < test_libs_->length(); i++) { | 
|  | if (strcmp(url, (*test_libs_)[i].url) == 0) { | 
|  | (*test_libs_)[i].source = source; | 
|  | return; | 
|  | } | 
|  | } | 
|  | TestLibEntry entry; | 
|  | entry.url = url; | 
|  | entry.source = source; | 
|  | test_libs_->Add(entry); | 
|  | } | 
|  |  | 
|  | const char* TestCase::GetTestLib(const char* url) { | 
|  | if (test_libs_ == nullptr) { | 
|  | return nullptr; | 
|  | } | 
|  | for (intptr_t i = 0; i < test_libs_->length(); i++) { | 
|  | if (strcmp(url, (*test_libs_)[i].url) == 0) { | 
|  | return (*test_libs_)[i].source; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | #ifndef PRODUCT | 
|  | static const char* kIsolateReloadTestLibSource = R"( | 
|  | @pragma("vm:external-name", "Test_Reload") | 
|  | external void reloadTest(); | 
|  | @pragma("vm:external-name", "Test_CollectNewSpace") | 
|  | external void collectNewSpace(); | 
|  | @pragma("vm:external-name", "Test_CollectOldSpace") | 
|  | external void collectOldSpace(); | 
|  | )"; | 
|  |  | 
|  | static const char* IsolateReloadTestLibUri() { | 
|  | return "test:isolate_reload_helper"; | 
|  | } | 
|  |  | 
|  | #define RELOAD_NATIVE_LIST(V)                                                  \ | 
|  | V(Test_Reload, 0)                                                            \ | 
|  | V(Test_CollectNewSpace, 0)                                                   \ | 
|  | V(Test_CollectOldSpace, 0) | 
|  |  | 
|  | RELOAD_NATIVE_LIST(DECLARE_FUNCTION); | 
|  |  | 
|  | static struct NativeEntries { | 
|  | const char* name_; | 
|  | Dart_NativeFunction function_; | 
|  | int argument_count_; | 
|  | } ReloadEntries[] = {RELOAD_NATIVE_LIST(REGISTER_FUNCTION)}; | 
|  |  | 
|  | static Dart_NativeFunction IsolateReloadTestNativeResolver( | 
|  | Dart_Handle name, | 
|  | int argument_count, | 
|  | bool* auto_setup_scope) { | 
|  | const char* function_name = nullptr; | 
|  | Dart_Handle result = Dart_StringToCString(name, &function_name); | 
|  | ASSERT(!Dart_IsError(result)); | 
|  | ASSERT(function_name != nullptr); | 
|  | ASSERT(auto_setup_scope != nullptr); | 
|  | *auto_setup_scope = true; | 
|  | int num_entries = sizeof(ReloadEntries) / sizeof(struct NativeEntries); | 
|  | for (int i = 0; i < num_entries; i++) { | 
|  | struct NativeEntries* entry = &(ReloadEntries[i]); | 
|  | if ((strcmp(function_name, entry->name_) == 0) && | 
|  | (entry->argument_count_ == argument_count)) { | 
|  | return reinterpret_cast<Dart_NativeFunction>(entry->function_); | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | void FUNCTION_NAME(Test_Reload)(Dart_NativeArguments native_args) { | 
|  | Dart_Handle result = TestCase::TriggerReload(/* kernel_buffer= */ nullptr, | 
|  | /* kernel_buffer_size= */ 0); | 
|  | if (Dart_IsError(result)) { | 
|  | Dart_PropagateError(result); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FUNCTION_NAME(Test_CollectNewSpace)(Dart_NativeArguments native_args) { | 
|  | TransitionNativeToVM transition(Thread::Current()); | 
|  | GCTestHelper::CollectNewSpace(); | 
|  | } | 
|  |  | 
|  | void FUNCTION_NAME(Test_CollectOldSpace)(Dart_NativeArguments native_args) { | 
|  | TransitionNativeToVM transition(Thread::Current()); | 
|  | GCTestHelper::CollectOldSpace(); | 
|  | } | 
|  |  | 
|  | #endif  // !PRODUCT | 
|  |  | 
|  | static void LoadIsolateReloadTestLibIfNeeded(const char* script) { | 
|  | #ifndef PRODUCT | 
|  | if (strstr(script, IsolateReloadTestLibUri()) != nullptr) { | 
|  | Dart_Handle result = TestCase::LoadTestLibrary( | 
|  | IsolateReloadTestLibUri(), kIsolateReloadTestLibSource, | 
|  | IsolateReloadTestNativeResolver); | 
|  | EXPECT_VALID(result); | 
|  | } | 
|  | #endif  // ifndef PRODUCT | 
|  | } | 
|  |  | 
|  | char* TestCase::CompileTestScriptWithDFE(const char* url, | 
|  | const char* source, | 
|  | const uint8_t** kernel_buffer, | 
|  | intptr_t* kernel_buffer_size, | 
|  | bool incrementally, | 
|  | const char* multiroot_filepaths, | 
|  | const char* multiroot_scheme) { | 
|  | // clang-format off | 
|  | Dart_SourceFile sourcefiles[] = { | 
|  | { | 
|  | url, source, | 
|  | }, | 
|  | { | 
|  | "file:///.packages", "" | 
|  | }}; | 
|  | // clang-format on | 
|  | return CompileTestScriptWithDFE( | 
|  | url, sizeof(sourcefiles) / sizeof(Dart_SourceFile), sourcefiles, | 
|  | kernel_buffer, kernel_buffer_size, incrementally, multiroot_filepaths, | 
|  | multiroot_scheme); | 
|  | } | 
|  |  | 
|  | char* TestCase::CompileTestScriptWithDFE(const char* url, | 
|  | int sourcefiles_count, | 
|  | Dart_SourceFile sourcefiles[], | 
|  | const uint8_t** kernel_buffer, | 
|  | intptr_t* kernel_buffer_size, | 
|  | bool incrementally, | 
|  | const char* multiroot_filepaths, | 
|  | const char* multiroot_scheme) { | 
|  | Zone* zone = Thread::Current()->zone(); | 
|  | Dart_KernelCompilationResult result = KernelIsolate::CompileToKernel( | 
|  | url, platform_dill, platform_dill_size, sourcefiles_count, sourcefiles, | 
|  | incrementally, /*for_snapshot=*/false, | 
|  | /*embed_sources=*/true, nullptr, multiroot_filepaths, multiroot_scheme); | 
|  | if (result.status == Dart_KernelCompilationStatus_Ok) { | 
|  | if (KernelIsolate::AcceptCompilation().status != | 
|  | Dart_KernelCompilationStatus_Ok) { | 
|  | FATAL( | 
|  | "An error occurred in the CFE while accepting the most recent" | 
|  | " compilation results."); | 
|  | } | 
|  | } | 
|  | return ValidateCompilationResult(zone, result, kernel_buffer, | 
|  | kernel_buffer_size); | 
|  | } | 
|  |  | 
|  | char* TestCase::ValidateCompilationResult( | 
|  | Zone* zone, | 
|  | Dart_KernelCompilationResult compilation_result, | 
|  | const uint8_t** kernel_buffer, | 
|  | intptr_t* kernel_buffer_size) { | 
|  | if (compilation_result.status != Dart_KernelCompilationStatus_Ok) { | 
|  | ASSERT(compilation_result.kernel == nullptr); | 
|  | char* result = | 
|  | OS::SCreate(zone, "Compilation failed %s", compilation_result.error); | 
|  | free(compilation_result.error); | 
|  | *kernel_buffer = nullptr; | 
|  | *kernel_buffer_size = 0; | 
|  | return result; | 
|  | } | 
|  | ASSERT(compilation_result.error == nullptr); | 
|  | *kernel_buffer = compilation_result.kernel; | 
|  | *kernel_buffer_size = compilation_result.kernel_size; | 
|  | if (kernel_buffer == nullptr) { | 
|  | return OS::SCreate(zone, "front end generated a nullptr kernel file"); | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | static Dart_Handle LibraryTagHandler(Dart_LibraryTag tag, | 
|  | Dart_Handle library, | 
|  | Dart_Handle url) { | 
|  | if (tag == Dart_kCanonicalizeUrl) { | 
|  | Dart_Handle library_url = Dart_LibraryUrl(library); | 
|  | if (Dart_IsError(library_url)) { | 
|  | return library_url; | 
|  | } | 
|  |  | 
|  | const char* library_url_cstr; | 
|  | Dart_Handle result = Dart_StringToCString(library_url, &library_url_cstr); | 
|  | if (Dart_IsError(result)) { | 
|  | return result; | 
|  | } | 
|  | const char* url_cstr; | 
|  | result = Dart_StringToCString(url, &url_cstr); | 
|  | if (Dart_IsError(result)) { | 
|  | return result; | 
|  | } | 
|  | CStringUniquePtr resolved_uri = ResolveUri(url_cstr, library_url_cstr); | 
|  | if (!resolved_uri) { | 
|  | return DartUtils::NewError("%s: Unable to canonicalize uri '%s'.", | 
|  | __FUNCTION__, url_cstr); | 
|  | } | 
|  | result = Dart_NewStringFromCString(resolved_uri.get()); | 
|  | return result; | 
|  | } | 
|  | UNREACHABLE(); | 
|  | return Dart_Null(); | 
|  | } | 
|  |  | 
|  | static intptr_t BuildSourceFilesArray( | 
|  | Dart_SourceFile** sourcefiles, | 
|  | const char* script, | 
|  | const char* script_url = RESOLVED_USER_TEST_URI) { | 
|  | ASSERT(sourcefiles != nullptr); | 
|  | ASSERT(script != nullptr); | 
|  |  | 
|  | intptr_t num_test_libs = 0; | 
|  | if (test_libs_ != nullptr) { | 
|  | num_test_libs = test_libs_->length(); | 
|  | } | 
|  |  | 
|  | *sourcefiles = new Dart_SourceFile[num_test_libs + 1]; | 
|  | (*sourcefiles)[0].uri = script_url; | 
|  | (*sourcefiles)[0].source = script; | 
|  | for (intptr_t i = 0; i < num_test_libs; ++i) { | 
|  | (*sourcefiles)[i + 1].uri = test_libs_->At(i).url; | 
|  | (*sourcefiles)[i + 1].source = test_libs_->At(i).source; | 
|  | } | 
|  | return num_test_libs + 1; | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::LoadTestScript(const char* script, | 
|  | Dart_NativeEntryResolver resolver, | 
|  | const char* lib_url, | 
|  | bool finalize_classes) { | 
|  | LoadIsolateReloadTestLibIfNeeded(script); | 
|  | Dart_SourceFile* sourcefiles = nullptr; | 
|  | intptr_t num_sources = BuildSourceFilesArray(&sourcefiles, script, lib_url); | 
|  | Dart_Handle result = LoadTestScriptWithDFE(num_sources, sourcefiles, resolver, | 
|  | finalize_classes, true); | 
|  | delete[] sourcefiles; | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static void MallocFinalizer(void* isolate_callback_data, void* peer) { | 
|  | free(peer); | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::LoadTestLibrary(const char* lib_uri, | 
|  | const char* script, | 
|  | Dart_NativeEntryResolver resolver) { | 
|  | LoadIsolateReloadTestLibIfNeeded(script); | 
|  | const char* prefixed_lib_uri = | 
|  | OS::SCreate(Thread::Current()->zone(), "file:///%s", lib_uri); | 
|  | Dart_SourceFile sourcefiles[] = {{prefixed_lib_uri, script}}; | 
|  | const uint8_t* kernel_buffer = nullptr; | 
|  | intptr_t kernel_buffer_size = 0; | 
|  | int sourcefiles_count = sizeof(sourcefiles) / sizeof(Dart_SourceFile); | 
|  | char* error = TestCase::CompileTestScriptWithDFE( | 
|  | sourcefiles[0].uri, sourcefiles_count, sourcefiles, &kernel_buffer, | 
|  | &kernel_buffer_size, true); | 
|  | if (error != nullptr) { | 
|  | return Dart_NewApiError(error); | 
|  | } | 
|  |  | 
|  | Dart_Handle td = Dart_NewExternalTypedDataWithFinalizer( | 
|  | Dart_TypedData_kUint8, const_cast<uint8_t*>(kernel_buffer), | 
|  | kernel_buffer_size, const_cast<uint8_t*>(kernel_buffer), | 
|  | kernel_buffer_size, MallocFinalizer); | 
|  | EXPECT_VALID(td); | 
|  | Dart_Handle lib = Dart_IsBytecode(kernel_buffer, kernel_buffer_size) | 
|  | ? Dart_LoadLibraryFromBytecode(td) | 
|  | : Dart_LoadLibrary(td); | 
|  | EXPECT_VALID(lib); | 
|  |  | 
|  | // TODO(32618): Kernel doesn't correctly represent the root library. | 
|  | lib = Dart_LookupLibrary(Dart_NewStringFromCString(sourcefiles[0].uri)); | 
|  | EXPECT_VALID(lib); | 
|  | Dart_Handle result = Dart_SetRootLibrary(lib); | 
|  | EXPECT_VALID(result); | 
|  |  | 
|  | Dart_SetNativeResolver(lib, resolver, nullptr); | 
|  | return lib; | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::LoadTestScriptWithDFE(int sourcefiles_count, | 
|  | Dart_SourceFile sourcefiles[], | 
|  | Dart_NativeEntryResolver resolver, | 
|  | bool finalize, | 
|  | bool incrementally, | 
|  | const char* entry_script_uri, | 
|  | const char* multiroot_filepaths, | 
|  | const char* multiroot_scheme) { | 
|  | // First script is the main script. | 
|  | Dart_Handle result = Dart_SetLibraryTagHandler(LibraryTagHandler); | 
|  | EXPECT_VALID(result); | 
|  | const uint8_t* kernel_buffer = nullptr; | 
|  | intptr_t kernel_buffer_size = 0; | 
|  | char* error = TestCase::CompileTestScriptWithDFE( | 
|  | entry_script_uri != nullptr ? entry_script_uri : sourcefiles[0].uri, | 
|  | sourcefiles_count, sourcefiles, &kernel_buffer, &kernel_buffer_size, | 
|  | incrementally, multiroot_filepaths, multiroot_scheme); | 
|  | if (error != nullptr) { | 
|  | return Dart_NewApiError(error); | 
|  | } | 
|  |  | 
|  | Dart_Handle td = Dart_NewExternalTypedDataWithFinalizer( | 
|  | Dart_TypedData_kUint8, const_cast<uint8_t*>(kernel_buffer), | 
|  | kernel_buffer_size, const_cast<uint8_t*>(kernel_buffer), | 
|  | kernel_buffer_size, MallocFinalizer); | 
|  | EXPECT_VALID(td); | 
|  | Dart_Handle lib = Dart_IsBytecode(kernel_buffer, kernel_buffer_size) | 
|  | ? Dart_LoadLibraryFromBytecode(td) | 
|  | : Dart_LoadLibrary(td); | 
|  | EXPECT_VALID(lib); | 
|  |  | 
|  | // BOGUS: Kernel doesn't correctly represent the root library. | 
|  | lib = Dart_LookupLibrary(Dart_NewStringFromCString( | 
|  | entry_script_uri != nullptr ? entry_script_uri : sourcefiles[0].uri)); | 
|  | EXPECT_VALID(lib); | 
|  | result = Dart_SetRootLibrary(lib); | 
|  | EXPECT_VALID(result); | 
|  |  | 
|  | result = Dart_SetNativeResolver(lib, resolver, nullptr); | 
|  | EXPECT_VALID(result); | 
|  | if (finalize) { | 
|  | result = Dart_FinalizeLoading(false); | 
|  | EXPECT_VALID(result); | 
|  | } | 
|  | return lib; | 
|  | } | 
|  |  | 
|  | #ifndef PRODUCT | 
|  |  | 
|  | Dart_Handle TestCase::SetReloadTestScript(const char* script) { | 
|  | // For our vm/cc/IsolateReload_* tests we flip the GC flag on, which will | 
|  | // cause the isolate reload to do GCs before/after morphing, etc. | 
|  | FLAG_gc_during_reload = true; | 
|  | FLAG_force_evacuation = true; | 
|  |  | 
|  | Dart_SourceFile* sourcefiles = nullptr; | 
|  | intptr_t num_files = BuildSourceFilesArray(&sourcefiles, script); | 
|  | Dart_KernelCompilationResult compilation_result = | 
|  | KernelIsolate::UpdateInMemorySources(num_files, sourcefiles); | 
|  | delete[] sourcefiles; | 
|  | if (compilation_result.status != Dart_KernelCompilationStatus_Ok) { | 
|  | Dart_Handle result = Dart_NewApiError(compilation_result.error); | 
|  | free(compilation_result.error); | 
|  | return result; | 
|  | } | 
|  | return Api::Success(); | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::TriggerReload( | 
|  | std::function<bool(IsolateGroup*, JSONStream*)> do_reload) { | 
|  | Thread* thread = Thread::Current(); | 
|  | IsolateGroup* isolate_group = thread->isolate_group(); | 
|  | JSONStream js; | 
|  | js.set_id_zone(thread->isolate()->EnsureDefaultServiceIdZone()); | 
|  | bool success = false; | 
|  | { | 
|  | TransitionNativeToVM transition(thread); | 
|  | success = do_reload(isolate_group, &js); | 
|  | OS::PrintErr("RELOAD REPORT:\n%s\n", js.ToCString()); | 
|  | } | 
|  |  | 
|  | Dart_Handle result = Dart_Null(); | 
|  | if (success) { | 
|  | result = Dart_FinalizeLoading(false); | 
|  | } | 
|  |  | 
|  | if (Dart_IsError(result)) { | 
|  | // Keep load error. | 
|  | } else if (isolate_group->reload_context()->reload_aborted()) { | 
|  | TransitionNativeToVM transition(thread); | 
|  | result = Api::NewHandle(thread, isolate_group->program_reload_context() | 
|  | ->group_reload_context() | 
|  | ->error()); | 
|  | } else { | 
|  | result = Dart_RootLibrary(); | 
|  | } | 
|  |  | 
|  | TransitionNativeToVM transition(thread); | 
|  | if (isolate_group->program_reload_context() != nullptr) { | 
|  | isolate_group->DeleteReloadContext(); | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::TriggerReload(const char* root_script_url) { | 
|  | return TriggerReload([&](IsolateGroup* isolate_group, JSONStream* js) { | 
|  | return isolate_group->ReloadSources(js, | 
|  | /*force_reload=*/false, root_script_url, | 
|  | /*packages_url=*/nullptr, | 
|  | /*dont_delete_reload_context=*/true); | 
|  | }); | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::TriggerReload(const uint8_t* kernel_buffer, | 
|  | intptr_t kernel_buffer_size) { | 
|  | return TriggerReload([&](IsolateGroup* isolate_group, JSONStream* js) { | 
|  | return isolate_group->ReloadKernel(js, | 
|  | /*force_reload=*/false, kernel_buffer, | 
|  | kernel_buffer_size, | 
|  | /*dont_delete_reload_context=*/true); | 
|  | }); | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::ReloadTestScript(const char* script) { | 
|  | Dart_SourceFile* sourcefiles = nullptr; | 
|  | intptr_t num_files = BuildSourceFilesArray(&sourcefiles, script); | 
|  | Dart_KernelCompilationResult compilation_result = | 
|  | KernelIsolate::UpdateInMemorySources(num_files, sourcefiles); | 
|  | delete[] sourcefiles; | 
|  | if (compilation_result.status != Dart_KernelCompilationStatus_Ok) { | 
|  | Dart_Handle result = Dart_NewApiError(compilation_result.error); | 
|  | free(compilation_result.error); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | return TriggerReload(/*kernel_buffer=*/nullptr, /*kernel_buffer_size=*/0); | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::ReloadTestKernel(const uint8_t* kernel_buffer, | 
|  | intptr_t kernel_buffer_size) { | 
|  | return TriggerReload(kernel_buffer, kernel_buffer_size); | 
|  | } | 
|  |  | 
|  | #endif  // !PRODUCT | 
|  |  | 
|  | Dart_Handle TestCase::LoadCoreTestScript(const char* script, | 
|  | Dart_NativeEntryResolver resolver) { | 
|  | return LoadTestScript(script, resolver, CORELIB_TEST_URI); | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::lib() { | 
|  | Dart_Handle url = NewString(TestCase::url()); | 
|  | Dart_Handle lib = Dart_LookupLibrary(url); | 
|  | EXPECT_VALID(lib); | 
|  | ASSERT(Dart_IsLibrary(lib)); | 
|  | return lib; | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::library_handler(Dart_LibraryTag tag, | 
|  | Dart_Handle library, | 
|  | Dart_Handle url) { | 
|  | if (tag == Dart_kCanonicalizeUrl) { | 
|  | return url; | 
|  | } | 
|  | return Api::Success(); | 
|  | } | 
|  |  | 
|  | Dart_Handle TestCase::EvaluateExpression(const Library& lib, | 
|  | const String& expr, | 
|  | const Array& param_names, | 
|  | const Array& param_values) { | 
|  | Thread* thread = Thread::Current(); | 
|  |  | 
|  | Object& val = Object::Handle(); | 
|  | if (!KernelIsolate::IsRunning()) { | 
|  | UNREACHABLE(); | 
|  | } else { | 
|  | Dart_KernelCompilationResult compilation_result = | 
|  | KernelIsolate::CompileExpressionToKernel( | 
|  | /* platform_kernel= */ nullptr, /* platform_kernel_size= */ 0, | 
|  | expr.ToCString(), param_names, Array::empty_array(), | 
|  | Array::empty_array(), Array::empty_array(), Array::empty_array(), | 
|  | String::Handle(lib.url()).ToCString(), | 
|  | /* klass= */ nullptr, | 
|  | /* method= */ nullptr, | 
|  | /* token_pos= */ TokenPosition::kNoSource, | 
|  | /* script_uri= */ String::Handle(lib.url()).ToCString(), | 
|  | /* is_static= */ true); | 
|  | if (compilation_result.status != Dart_KernelCompilationStatus_Ok) { | 
|  | return Api::NewError("%s", compilation_result.error); | 
|  | } | 
|  |  | 
|  | const ExternalTypedData& kernel_buffer = | 
|  | ExternalTypedData::Handle(ExternalTypedData::NewFinalizeWithFree( | 
|  | const_cast<uint8_t*>(compilation_result.kernel), | 
|  | compilation_result.kernel_size)); | 
|  |  | 
|  | val = lib.EvaluateCompiledExpression(kernel_buffer, Array::empty_array(), | 
|  | param_values, | 
|  | TypeArguments::null_type_arguments()); | 
|  | } | 
|  | return Api::NewHandle(thread, val.ptr()); | 
|  | } | 
|  |  | 
|  | #if !defined(PRODUCT) && (defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64)) | 
|  | static bool IsHex(int c) { | 
|  | return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f'); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void AssemblerTest::Assemble() { | 
|  | auto thread = Thread::Current(); | 
|  | const String& function_name = String::ZoneHandle(Symbols::New(thread, name_)); | 
|  |  | 
|  | // We make a dummy script so that exception objects can be composed for | 
|  | // assembler instructions that do runtime calls. | 
|  | const char* kDummyScript = "assembler_test_dummy_function() {}"; | 
|  | const Script& script = Script::Handle( | 
|  | Script::New(function_name, String::Handle(String::New(kDummyScript)))); | 
|  | const Library& lib = Library::Handle(Library::CoreLibrary()); | 
|  | const Class& cls = Class::ZoneHandle( | 
|  | Class::New(lib, function_name, script, TokenPosition::kMinSource)); | 
|  | const FunctionType& signature = FunctionType::ZoneHandle(FunctionType::New()); | 
|  | Function& function = Function::ZoneHandle(Function::New( | 
|  | signature, function_name, UntaggedFunction::kRegularFunction, true, false, | 
|  | false, false, false, cls, TokenPosition::kMinSource)); | 
|  | SafepointWriteRwLocker ml(thread, thread->isolate_group()->program_lock()); | 
|  | code_ = Code::FinalizeCodeAndNotify(function, nullptr, assembler_, | 
|  | Code::PoolAttachment::kAttachPool); | 
|  | code_.set_owner(function); | 
|  | code_.set_exception_handlers(Object::empty_exception_handlers()); | 
|  | #ifndef PRODUCT | 
|  | // Disassemble relative since code addresses are not stable from run to run. | 
|  | SetFlagScope<bool> sfs(&FLAG_disassemble_relative, true); | 
|  | uword start = code_.PayloadStart(); | 
|  | uword end = start + assembler_->CodeSize(); | 
|  | #if defined(TARGET_ARCH_RISCV32) || defined(TARGET_ARCH_RISCV64) | 
|  | Disassembler::SetExtensions(assembler_->extensions()); | 
|  | #endif | 
|  | if (FLAG_disassemble) { | 
|  | OS::PrintErr("Code for test '%s' {\n", name_); | 
|  | Disassembler::Disassemble(start, end); | 
|  | OS::PrintErr("}\n"); | 
|  | } | 
|  | Disassembler::Disassemble(start, end, disassembly_, DISASSEMBLY_SIZE); | 
|  | #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) | 
|  | // Blank out absolute addressing constants on ia32, since they are not stable | 
|  | // from run to run. | 
|  | // Blank out thread-relative offsets on x64 since they change when new fields | 
|  | // are added to thread object. | 
|  | bool in_hex_constant = false; | 
|  | for (char* p = disassembly_; *p != '\0'; p++) { | 
|  | if (in_hex_constant) { | 
|  | if (IsHex(*p)) { | 
|  | *p = '.'; | 
|  | } else { | 
|  | in_hex_constant = false; | 
|  | } | 
|  | } else { | 
|  | #if defined(TARGET_ARCH_IA32) | 
|  | if (*p == '[' && *(p + 1) == '0' && *(p + 2) == 'x' && IsHex(*(p + 3)) && | 
|  | IsHex(*(p + 4))) { | 
|  | p += 2; | 
|  | in_hex_constant = true; | 
|  | } | 
|  | #endif  // defined(TARGET_ARCH_IA32) | 
|  | #if defined(TARGET_ARCH_X64) | 
|  | if (*p == '[' && *(p + 1) == 't' && *(p + 2) == 'h' && *(p + 3) == 'r' && | 
|  | *(p + 4) == '+' && *(p + 5) == '0' && *(p + 6) == 'x' && | 
|  | IsHex(*(p + 7)) && IsHex(*(p + 8))) { | 
|  | p += 6; | 
|  | in_hex_constant = true; | 
|  | } | 
|  | #endif  // defined(TARGET_ARCH_X64) | 
|  | } | 
|  | } | 
|  | #endif  // defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_X64) | 
|  | #endif  // !PRODUCT | 
|  | } | 
|  |  | 
|  | const Code& AssemblerTest::Generate( | 
|  | const char* name, | 
|  | const std::function<void(compiler::Assembler* assembler)>& generator) { | 
|  | compiler::ObjectPoolBuilder object_pool_builder; | 
|  | compiler::Assembler assembler(&object_pool_builder, /*far_branch_level=*/0); | 
|  | AssemblerTest test(name, &assembler, Thread::Current()->zone()); | 
|  | assembler.Ret(); | 
|  | test.Assemble(); | 
|  | return test.code(); | 
|  | } | 
|  |  | 
|  | bool CompilerTest::TestCompileFunction(const Function& function) { | 
|  | Thread* thread = Thread::Current(); | 
|  | ASSERT(thread != nullptr); | 
|  | ASSERT(ClassFinalizer::AllClassesFinalized()); | 
|  | const Object& result = | 
|  | Object::Handle(Compiler::CompileFunction(thread, function)); | 
|  | return result.IsCode(); | 
|  | } | 
|  |  | 
|  | void ElideJSONSubstring(const char* prefix, | 
|  | const char* in, | 
|  | char* out, | 
|  | const char* postfix) { | 
|  | const char* pos = strstr(in, prefix); | 
|  | while (pos != nullptr) { | 
|  | // Copy up to pos into the output buffer. | 
|  | while (in < pos) { | 
|  | *out++ = *in++; | 
|  | } | 
|  |  | 
|  | // Skip to the closing postfix. | 
|  | in += strlen(prefix); | 
|  | in += strcspn(in, postfix); | 
|  | pos = strstr(in, prefix); | 
|  | } | 
|  | // Copy the remainder of in to out. | 
|  | while (*in != '\0') { | 
|  | *out++ = *in++; | 
|  | } | 
|  | *out = '\0'; | 
|  | } | 
|  |  | 
|  | void StripTokenPositions(char* buffer) { | 
|  | ElideJSONSubstring(",\"tokenPos\":", buffer, buffer, ","); | 
|  | ElideJSONSubstring(",\"endTokenPos\":", buffer, buffer, "}"); | 
|  | } | 
|  |  | 
|  | }  // namespace dart |