| // 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 kPlatformStrongDill[]; |
| extern intptr_t kPlatformStrongDillSize; |
| } |
| |
| namespace dart { |
| |
| DECLARE_FLAG(bool, gc_during_reload); |
| DECLARE_FLAG(bool, force_evacuation); |
| |
| const uint8_t* platform_strong_dill = kPlatformStrongDill; |
| const intptr_t platform_strong_dill_size = kPlatformStrongDillSize; |
| |
| 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); |
| 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, |
| bool allow_compile_errors, |
| 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, allow_compile_errors, |
| 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, |
| bool allow_compile_errors, |
| const char* multiroot_filepaths, |
| const char* multiroot_scheme) { |
| Zone* zone = Thread::Current()->zone(); |
| Dart_KernelCompilationResult result = KernelIsolate::CompileToKernel( |
| url, platform_strong_dill, platform_strong_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, allow_compile_errors); |
| } |
| |
| char* TestCase::ValidateCompilationResult( |
| Zone* zone, |
| Dart_KernelCompilationResult compilation_result, |
| const uint8_t** kernel_buffer, |
| intptr_t* kernel_buffer_size, |
| bool allow_compile_errors) { |
| if (!allow_compile_errors && |
| (compilation_result.status != Dart_KernelCompilationStatus_Ok)) { |
| char* result = |
| OS::SCreate(zone, "Compilation failed %s", compilation_result.error); |
| free(compilation_result.error); |
| if (compilation_result.kernel != nullptr) { |
| free(const_cast<uint8_t*>(compilation_result.kernel)); |
| } |
| *kernel_buffer = nullptr; |
| *kernel_buffer_size = 0; |
| return result; |
| } |
| *kernel_buffer = compilation_result.kernel; |
| *kernel_buffer_size = compilation_result.kernel_size; |
| if (compilation_result.error != nullptr) { |
| free(compilation_result.error); |
| } |
| 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::LoadTestScriptWithErrors( |
| const char* script, |
| Dart_NativeEntryResolver resolver, |
| const char* lib_url, |
| bool finalize_classes) { |
| return LoadTestScript(script, resolver, lib_url, finalize_classes, true); |
| } |
| |
| Dart_Handle TestCase::LoadTestScript(const char* script, |
| Dart_NativeEntryResolver resolver, |
| const char* lib_url, |
| bool finalize_classes, |
| bool allow_compile_errors) { |
| 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, allow_compile_errors); |
| 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 ((kernel_buffer == nullptr) && (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_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, |
| bool allow_compile_errors, |
| 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, allow_compile_errors, multiroot_filepaths, |
| multiroot_scheme); |
| if ((kernel_buffer == nullptr) && 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_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); |
| if (compilation_result.kernel != nullptr) { |
| free(const_cast<uint8_t*>(compilation_result.kernel)); |
| } |
| 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 |