d36adbacaf
The former contents of the VM isolate are now included into each isolate group. This makes each isolate group's heap independent, and in particular allows each heap to be allocated to a separate pointer cage (not done in this CL). The duplicated stubs that allowed PC relative calls are removed, since the originals can now be the target of PC relative calls. The bootstrapping needing to load an AppJIT or AppAOT snapshot is reduced to allocating the oddballs. The code is entirely dropped in the AOT runtime, but the JIT runtime still has it to allow for flags to affect the compilation of the stub code. Further refactoring might be able to remove this for the JIT runtime too, with only gen_snapshot knowing how to bootstrap. Class serialization no longer distinguishes predefined classes. The page containing null is marked as never-evacuate. null, false and true must not move because the compiler relies on their low bits having certain patterns for some optimizations. (Previously, the entire VM isolate heap never moved.) Compaction is disabled for IA32. Due to register pressure, some stub calls must not use a scratch register and embed the address of Code. The page containing the call-through-safepoint stub is frozen when running with --write-protect-code and the stub is created at runtime (instead of loaded from an AppJIT or AppAOT snapshot). This stub must remain executable even during a safepoint, as a foreign call might during return during a safepoint and only block after the stub directs it to the runtime. The snapshot symbols are renamed to kDartSnapshotData and kDartSnapshotText. There is no need to distinguish the VM isolate's snapshot, and snaphots are per isolate group not per isolate. Aliases with the old names are added to ease migration. Some global flags that were automatically set based on the VM isolate's snapshot are now isolate group flags and automatically set by the isolate group's snapshot. TEST=ci Change-Id: Iee82016057d609112e9b021d178fc3d4d18b5044 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/500621 Reviewed-by: Alexander Markov <alexmarkov@google.com> Reviewed-by: Tess Strickland <sstrickl@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
803 lines
28 KiB
C++
803 lines
28 KiB
C++
// 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
|