| // 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; | |
| 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_snapshot_data, | |
| 0 /* Snapshots have length encoded within them. */, | |
| bin::core_snapshot_text, 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::SetupCoreLibraries( | |
| /*is_service_isolate=*/false, | |
| /*trace_loading=*/false, | |
| /*flag_profile_microtasks=*/false, bin::DartIoSettings{}); | |
| 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 |