Dart Engine
Adds shared libraries for embedding Dart VM and new API (runtime/engine/include/dart_engine.h). TEST=tests/standalone/embedder_samples_test.dart Cq-Include-Trybots: luci.dart.try:vm-aot-android-release-arm64c-try,vm-aot-android-release-arm_x64-try,vm-aot-asan-linux-release-x64-try,vm-aot-dwarf-linux-product-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-linux-debug-simarm_x64-try,vm-aot-linux-debug-simriscv32-try,vm-aot-linux-debug-simriscv64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-debug-x64c-try,vm-aot-linux-product-x64-try,vm-aot-linux-release-arm64-try,vm-aot-linux-release-simarm_x64-try,vm-aot-linux-release-x64-try,vm-aot-mac-product-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-msan-linux-release-x64-try,vm-aot-obfuscate-linux-release-x64-try,vm-aot-optimization-level-linux-release-x64-try,vm-aot-tsan-linux-release-x64-try,vm-aot-ubsan-linux-release-x64-try,vm-aot-win-debug-x64-try,vm-aot-win-debug-x64c-try,vm-aot-win-product-x64-try,vm-aot-win-release-x64-try,vm-appjit-linux-debug-x64-try,vm-appjit-linux-product-x64-try,vm-appjit-linux-release-x64-try,vm-asan-linux-release-arm64-try,vm-asan-linux-release-x64-try,vm-checked-mac-release-arm64-try,vm-eager-optimization-linux-release-ia32-try,vm-eager-optimization-linux-release-x64-try,vm-ffi-android-debug-arm-try,vm-ffi-android-debug-arm64c-try,vm-ffi-android-product-arm-try,vm-ffi-android-product-arm64c-try,vm-ffi-android-release-arm-try,vm-ffi-android-release-arm64c-try,vm-ffi-qemu-linux-release-arm-try,vm-ffi-qemu-linux-release-riscv64-try,vm-fuchsia-release-arm64-try,vm-fuchsia-release-x64-try,vm-gcc-linux-try,vm-linux-debug-ia32-try,vm-linux-debug-simriscv32-try,vm-linux-debug-simriscv64-try,vm-linux-debug-x64-try,vm-linux-debug-x64c-try,vm-linux-release-arm64-try,vm-linux-release-ia32-try,vm-linux-release-simarm-try,vm-linux-release-x64-try,vm-mac-debug-arm64-try,vm-mac-debug-x64-try,vm-mac-release-arm64-try,vm-mac-release-x64-try,vm-msan-linux-release-arm64-try,vm-msan-linux-release-x64-try,vm-msvc-windows-try,vm-reload-linux-debug-x64-try,vm-reload-linux-release-x64-try,vm-reload-rollback-linux-debug-x64-try,vm-reload-rollback-linux-release-x64-try,vm-tsan-linux-release-arm64-try,vm-tsan-linux-release-x64-try,vm-ubsan-linux-release-arm64-try,vm-ubsan-linux-release-x64-try,vm-win-debug-x64-try,vm-win-debug-x64c-try,vm-win-release-ia32-try,vm-win-release-x64-try Change-Id: Ia4e4d1b871ddef515cfb2f4639bdaa9fe3676936 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/402860 Reviewed-by: Slava Egorov <vegorov@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com> Commit-Queue: Martin Kustermann <kustermann@google.com>
This commit is contained in:
committed by
Commit Queue
parent
79df09999e
commit
6e33c95463
@@ -52,7 +52,7 @@ group("runtime") {
|
||||
"runtime/bin:process_test",
|
||||
"runtime/bin:run_vm_tests",
|
||||
"runtime/vm:kernel_platform_files($host_toolchain)",
|
||||
"samples/embedder:all",
|
||||
"samples/embedder:kernel",
|
||||
"samples/ffi/http:fake_http",
|
||||
"utils/dartdev:dartdev",
|
||||
"utils/kernel-service:kernel-service",
|
||||
@@ -67,6 +67,12 @@ group("runtime") {
|
||||
deps += [ "utils/kernel-service:frontend_server" ]
|
||||
}
|
||||
|
||||
# AOT samples use dlopen to load AOT snapshots and it works only on
|
||||
# 64-bit Linux right now.
|
||||
if (is_linux && (dart_target_arch == "x64" || dart_target_arch == "arm64")) {
|
||||
deps += [ "samples/embedder:aot" ]
|
||||
}
|
||||
|
||||
if (!is_win) {
|
||||
# The test isn't run on windows
|
||||
deps += [ "runtime/bin:entrypoints_verification_test" ]
|
||||
|
||||
@@ -577,6 +577,7 @@ trace to find the place to insert the appropriate support.
|
||||
'--no-embed-sources',
|
||||
'--no-link-platform',
|
||||
'--enable-asserts',
|
||||
'--link-platform',
|
||||
]:
|
||||
pass
|
||||
elif self.get_option(['--depfile']):
|
||||
|
||||
@@ -736,7 +736,10 @@ source_set("dart_snapshot_cc") {
|
||||
}
|
||||
|
||||
source_set("dart_kernel_platform_cc") {
|
||||
visibility = [ ":*" ]
|
||||
visibility = [
|
||||
":*",
|
||||
"../engine:*",
|
||||
]
|
||||
deps = [
|
||||
":kernel_service_dill_linkable",
|
||||
":platform_strong_dill_linkable",
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# Copyright (c) 2025, 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.
|
||||
|
||||
source_set("engine_jit_set") {
|
||||
include_dirs = [
|
||||
".",
|
||||
"..",
|
||||
"//third_party/boringssl/src/include",
|
||||
"//third_party",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"../bin/dart_embedder_api_impl.cc",
|
||||
"../bin/vmservice_impl.cc",
|
||||
"../bin/vmservice_impl.h",
|
||||
"dart_engine_impl.cc",
|
||||
"engine.cc",
|
||||
"engine.h",
|
||||
]
|
||||
|
||||
configs += [
|
||||
"..:dart_arch_config",
|
||||
"..:dart_config",
|
||||
"..:dart_os_config",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"..:libdart_jit",
|
||||
"../bin:dart_io_api",
|
||||
"../bin:dart_kernel_platform_cc",
|
||||
"../bin:libdart_builtin",
|
||||
]
|
||||
}
|
||||
|
||||
source_set("engine_aot_set") {
|
||||
include_dirs = [
|
||||
".",
|
||||
"..",
|
||||
"//third_party/boringssl/src/include",
|
||||
"//third_party",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"../bin/dart_embedder_api_impl.cc",
|
||||
"../bin/vmservice_impl.cc",
|
||||
"../bin/vmservice_impl.h",
|
||||
"dart_engine_impl.cc",
|
||||
"engine.cc",
|
||||
"engine.h",
|
||||
]
|
||||
|
||||
configs += [
|
||||
"..:dart_aotruntime_config",
|
||||
"..:dart_arch_config",
|
||||
"..:dart_config",
|
||||
"..:dart_os_config",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"..:libdart_aotruntime",
|
||||
"../bin:dart_io_api",
|
||||
"../bin:libdart_builtin",
|
||||
]
|
||||
}
|
||||
|
||||
shared_library("dart_engine_jit_shared") {
|
||||
deps = [ ":engine_jit_set" ]
|
||||
|
||||
if (is_mac) {
|
||||
ldflags = [ "-Wl,-install_name,@rpath/libdart_engine_jit_shared.dylib" ]
|
||||
}
|
||||
|
||||
if (is_win) {
|
||||
ldflags = [ "/EXPORT:Dart_True" ]
|
||||
|
||||
libs = [
|
||||
"ole32.lib",
|
||||
"iphlpapi.lib",
|
||||
"psapi.lib",
|
||||
"ws2_32.lib",
|
||||
"Rpcrt4.lib",
|
||||
"shlwapi.lib",
|
||||
"winmm.lib",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
shared_library("dart_engine_aot_shared") {
|
||||
deps = [ ":engine_aot_set" ]
|
||||
|
||||
if (is_mac) {
|
||||
ldflags = [ "-Wl,-install_name,@rpath/libdart_engine_aot_shared.dylib" ]
|
||||
}
|
||||
|
||||
if (is_win) {
|
||||
ldflags = [ "/EXPORT:Dart_True" ]
|
||||
|
||||
libs = [
|
||||
"ole32.lib",
|
||||
"iphlpapi.lib",
|
||||
"psapi.lib",
|
||||
"ws2_32.lib",
|
||||
"Rpcrt4.lib",
|
||||
"shlwapi.lib",
|
||||
"winmm.lib",
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
# Dart Engine
|
||||
|
||||
The `include/dart_engine.h` provides additional functions to embed the Dart VM
|
||||
and to call Dart functions from kernel and AOT snapshots. It is not intended to
|
||||
be a full-featured API, and should be used together with `dart_api.h`.
|
||||
|
||||
It is intended for reusing of existing Dart code in non-Dart programs, and
|
||||
allows to use Dart snapshots as shared libraries: the caller can start one or
|
||||
several isolates from Dart snapshots and call Dart functions from it.
|
||||
|
||||
Comparing to `dart_api.h` it brings the following:
|
||||
|
||||
- Full initialization of Dart VM, including initializing core libraries.
|
||||
- Easier handling of isolate messages
|
||||
- Lock-guarded functions to enter/exit isolates.
|
||||
|
||||
See examples in `samples/embedder` for usages of the API.
|
||||
|
||||
## Handling isolate messages
|
||||
|
||||
In `dart_api.h` there are two possible ways to handle asynchronous
|
||||
isolate messages:
|
||||
|
||||
- `Dart_MessageNotifyCallback`: Users can pass a callback to get
|
||||
notifications about messages to handle, but handling messages still
|
||||
requires entering an isolate, entering a scope, calling
|
||||
`Dart_HandleMessage`, and then leaving a scope and an isolate.
|
||||
- `Dart_RunLoop`: Users can invoke it on a separate thread, but then they won't
|
||||
be able to enter isolates from other threads.
|
||||
|
||||
As an alternative, `dart_engine.h` provides a simpler function
|
||||
`DartEngine_HandleMessage`, which encapsulates isolate and scope management, and
|
||||
it allows to specify per isolate / default message schedulers, which only need
|
||||
to schedule an execution `DartEngine_HandleMessage` with the right isolate.
|
||||
|
||||
Users can create their own `DartEngine_MessageScheduler` struct and pass it to
|
||||
`DartEngine_SetMessageScheduler` / `DartEngine_SetDefaultMessageScheduler`. See
|
||||
`samples/embedder/run_timer_async.cc` and `samples/embedder/run_timer.cc`
|
||||
examples.
|
||||
|
||||
## Entering / leaving isolates
|
||||
|
||||
Because engine API uses its own message handling, it is important to use
|
||||
`DartEngine_AcquireIsolate` / `DartEngine_ReleaseIsolate` instead of
|
||||
`Dart_EnterIsolate` / `Dart_ExitIsolate`. `DartEngine_AcquireIsolate` blocks
|
||||
until an isolate can be entered by trying to obtain an internal lock (which is
|
||||
released by `DartEngine_ReleaseIsolate`), while `Dart_EnterIsolate` crashes if
|
||||
some other thread has entered the same isolate.
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2025, 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 <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "engine/engine.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_engine.h"
|
||||
#include "platform/utils.h"
|
||||
|
||||
namespace dart {
|
||||
namespace engine {
|
||||
|
||||
DART_EXPORT bool DartEngine_Init(char** error) {
|
||||
return Engine::instance()->Initialize(error);
|
||||
}
|
||||
|
||||
DART_EXPORT void DartEngine_Shutdown() {
|
||||
Engine::instance()->Shutdown();
|
||||
}
|
||||
|
||||
DART_EXPORT DartEngine_SnapshotData DartEngine_KernelFromFile(const char* path,
|
||||
char** error) {
|
||||
return Engine::instance()->KernelFromFile(path, error);
|
||||
}
|
||||
|
||||
DART_EXPORT DartEngine_SnapshotData
|
||||
DartEngine_AotSnapshotFromFile(const char* path, char** error) {
|
||||
return Engine::instance()->AotFromFile(path, error);
|
||||
}
|
||||
|
||||
DART_EXPORT Dart_Isolate
|
||||
DartEngine_CreateIsolate(DartEngine_SnapshotData snapshot_data, char** error) {
|
||||
if (!Engine::instance()->Initialize(error)) {
|
||||
return nullptr;
|
||||
}
|
||||
return Engine::instance()->StartIsolate(snapshot_data, error);
|
||||
}
|
||||
|
||||
DART_EXPORT void DartEngine_AcquireIsolate(Dart_Isolate isolate) {
|
||||
Engine::instance()->LockIsolate(isolate);
|
||||
Dart_EnterIsolate(isolate);
|
||||
}
|
||||
|
||||
DART_EXPORT void DartEngine_ReleaseIsolate() {
|
||||
Dart_Isolate current = Dart_CurrentIsolate();
|
||||
ASSERT(current != nullptr);
|
||||
Dart_ExitIsolate();
|
||||
Engine::instance()->UnlockIsolate(current);
|
||||
}
|
||||
|
||||
DART_EXPORT void DartEngine_SetHandleMessageErrorCallback(
|
||||
DartEngine_HandleMessageErrorCallback handle_message_error_callback) {
|
||||
Engine::instance()->SetHandleMessageErrorCallback(
|
||||
handle_message_error_callback);
|
||||
}
|
||||
|
||||
DART_EXPORT void DartEngine_SetDefaultMessageScheduler(
|
||||
DartEngine_MessageScheduler scheduler) {
|
||||
Engine::instance()->SetDefaultMessageScheduler(scheduler);
|
||||
}
|
||||
|
||||
DART_EXPORT void DartEngine_SetMessageScheduler(
|
||||
DartEngine_MessageScheduler scheduler,
|
||||
Dart_Isolate isolate) {
|
||||
Engine::instance()->SetMessageScheduler(scheduler, isolate);
|
||||
}
|
||||
|
||||
DART_EXPORT void DartEngine_HandleMessage(Dart_Isolate isolate) {
|
||||
Engine::instance()->HandleMessage(isolate);
|
||||
}
|
||||
|
||||
} // namespace engine
|
||||
} // namespace dart
|
||||
@@ -0,0 +1,349 @@
|
||||
// Copyright (c) 2025, 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 "engine/engine.h"
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "bin/dartutils.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_embedder_api.h"
|
||||
#include "include/dart_engine.h"
|
||||
#include "platform/lockers.h"
|
||||
#include "platform/syslog.h"
|
||||
#include "platform/utils.h"
|
||||
|
||||
namespace dart {
|
||||
namespace engine {
|
||||
|
||||
using platform::MutexLocker;
|
||||
|
||||
Engine* Engine::instance() {
|
||||
static Engine* instance = new Engine();
|
||||
return instance;
|
||||
}
|
||||
|
||||
DartEngine_SnapshotData Engine::KernelFromFile(const char* path, char** error) {
|
||||
DartEngine_SnapshotData result;
|
||||
result.kind = DartEngine_SnapshotKind_Kernel;
|
||||
result.script_uri = Utils::SCreate("file://%s", path);
|
||||
FILE* file = fopen(path, "rb");
|
||||
if (file == nullptr) {
|
||||
*error = Utils::SCreate("Error %d", errno);
|
||||
return result;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
intptr_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* buffer = reinterpret_cast<char*>(malloc(sizeof(char) * size));
|
||||
|
||||
intptr_t bytes_read = fread(buffer, 1, size, file);
|
||||
fclose(file);
|
||||
if (bytes_read != size) {
|
||||
free(buffer);
|
||||
*error = Utils::SCreate("Error reading %s: read %zu bytes instead of %zu",
|
||||
path, bytes_read, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
result.kernel_buffer_size = size;
|
||||
result.kernel_buffer = reinterpret_cast<uint8_t*>(buffer);
|
||||
|
||||
{
|
||||
MutexLocker ml(&engine_state_);
|
||||
owned_snapshots_.emplace_back(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
DartEngine_SnapshotData Engine::AotFromFile(const char* path, char** error) {
|
||||
DartEngine_SnapshotData result;
|
||||
result.kind = DartEngine_SnapshotKind_AOT;
|
||||
result.script_uri = Utils::SCreate("file://%s", path);
|
||||
|
||||
void* library =
|
||||
Utils::LoadDynamicLibrary(path, /*search_dll_load_dir=*/false, error);
|
||||
|
||||
result.vm_snapshot_data = reinterpret_cast<const uint8_t*>(
|
||||
Utils::ResolveSymbolInDynamicLibrary(library, kVmSnapshotDataCSymbol));
|
||||
result.vm_snapshot_instructions =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kVmSnapshotInstructionsCSymbol));
|
||||
result.vm_isolate_data =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kIsolateSnapshotDataCSymbol));
|
||||
result.vm_isolate_instructions =
|
||||
reinterpret_cast<const uint8_t*>(Utils::ResolveSymbolInDynamicLibrary(
|
||||
library, kIsolateSnapshotInstructionsCSymbol));
|
||||
|
||||
if (*error != nullptr) {
|
||||
return result;
|
||||
}
|
||||
|
||||
{
|
||||
MutexLocker ml(&engine_state_);
|
||||
loaded_libraries_.emplace_back(library);
|
||||
owned_snapshots_.emplace_back(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
Dart_InitializeParams CreateInitializeParams() {
|
||||
Dart_InitializeParams params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
|
||||
params.shutdown_isolate = nullptr;
|
||||
params.create_group = nullptr;
|
||||
return params;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool Engine::Initialize(char** error) {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
MutexLocker ml(&engine_lifecycle_);
|
||||
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
if (!dart::embedder::InitOnce(error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<const char*> flags{};
|
||||
if (Dart_IsPrecompiledRuntime()) {
|
||||
flags.push_back("--precompilation");
|
||||
}
|
||||
*error = Dart_SetVMFlags(flags.size(), flags.data());
|
||||
|
||||
if (*error != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Dart_Isolate Engine::StartIsolate(DartEngine_SnapshotData snapshot,
|
||||
char** error) {
|
||||
if (Dart_IsPrecompiledRuntime() &&
|
||||
snapshot.kind != DartEngine_SnapshotKind_AOT) {
|
||||
*error = Utils::StrDup("AOT Dart VM supports only AOT snapshots");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!Dart_IsPrecompiledRuntime() &&
|
||||
snapshot.kind != DartEngine_SnapshotKind_Kernel) {
|
||||
*error = Utils::StrDup("AOT Dart VM supports only AOT snapshots");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!first_isolate_started_) {
|
||||
// This is a part of an initialization, so using engine_lifecycle_ mutex
|
||||
// here.
|
||||
MutexLocker ml(&engine_lifecycle_);
|
||||
if (!first_isolate_started_) {
|
||||
Dart_InitializeParams initialize_params = CreateInitializeParams();
|
||||
|
||||
if (Dart_IsPrecompiledRuntime()) {
|
||||
initialize_params.vm_snapshot_data = snapshot.vm_snapshot_data;
|
||||
initialize_params.vm_snapshot_instructions =
|
||||
snapshot.vm_snapshot_instructions;
|
||||
}
|
||||
*error = Dart_Initialize(&initialize_params);
|
||||
if (*error != nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
first_isolate_started_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can start an isolate.
|
||||
Dart_IsolateFlags isolate_flags;
|
||||
Dart_IsolateFlagsInitialize(&isolate_flags);
|
||||
|
||||
Dart_Isolate isolate;
|
||||
if (Dart_IsPrecompiledRuntime()) {
|
||||
// Automatically sets the root library for the isolate.
|
||||
isolate = Dart_CreateIsolateGroup(
|
||||
snapshot.script_uri, strrchr(snapshot.script_uri, '/'),
|
||||
snapshot.vm_isolate_data, snapshot.vm_isolate_instructions,
|
||||
&isolate_flags, nullptr, nullptr, error);
|
||||
} else {
|
||||
isolate = Dart_CreateIsolateGroupFromKernel(
|
||||
snapshot.script_uri, snapshot.script_uri, snapshot.kernel_buffer,
|
||||
snapshot.kernel_buffer_size, &isolate_flags, nullptr, nullptr, error);
|
||||
}
|
||||
|
||||
if (*error != nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Dart_SetMessageNotifyCallback(Engine::MessageNotifyCallback);
|
||||
|
||||
Dart_EnterScope();
|
||||
|
||||
// In fact, this call initializes core libraries, (e.g. `print` doesn't work
|
||||
// without it).
|
||||
Dart_Handle core_libs_result =
|
||||
bin::DartUtils::PrepareForScriptLoading(false, false);
|
||||
if (Dart_IsError(core_libs_result)) {
|
||||
Dart_ShutdownIsolate();
|
||||
*error = Utils::StrDup(Dart_GetError(core_libs_result));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!Dart_IsPrecompiledRuntime()) {
|
||||
// In kernel mode, also call LoadScriptFromKernel to set the root library.
|
||||
// Technically, the library is already loaded after
|
||||
// Dart_CreateIsolateGroupFromKernel, the problem is we don't know its URI
|
||||
// (it is not related to snapshot->uri and depends on where the kernel
|
||||
// snapshot was built, e.g. file:///Users/user/samples/hello.dart)
|
||||
Dart_Handle library = Dart_LoadScriptFromKernel(
|
||||
snapshot.kernel_buffer, snapshot.kernel_buffer_size);
|
||||
|
||||
if (Dart_IsError(library)) {
|
||||
Dart_ShutdownIsolate();
|
||||
*error = Utils::StrDup(Dart_GetError(library));
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Dart_ExitScope();
|
||||
Dart_ExitIsolate();
|
||||
is_running_ = true;
|
||||
isolates_.emplace_back(isolate);
|
||||
return isolate;
|
||||
}
|
||||
|
||||
void Engine::MessageNotifyCallback(Dart_Isolate isolate) {
|
||||
Engine::instance()->NotifyMessage(isolate);
|
||||
}
|
||||
|
||||
void Engine::Shutdown() {
|
||||
MutexLocker shutdown_locker(&engine_lifecycle_);
|
||||
|
||||
is_running_ = false;
|
||||
for (auto isolate : isolates_) {
|
||||
LockIsolate(isolate);
|
||||
Dart_EnterIsolate(isolate);
|
||||
Dart_SetMessageNotifyCallback(nullptr);
|
||||
Dart_ShutdownIsolate();
|
||||
UnlockIsolate(isolate);
|
||||
}
|
||||
|
||||
MutexLocker state_locker(&engine_state_);
|
||||
for (auto& snapshot : owned_snapshots_) {
|
||||
switch (snapshot.kind) {
|
||||
case DartEngine_SnapshotKind_Kernel:
|
||||
free(const_cast<uint8_t*>(snapshot.kernel_buffer));
|
||||
break;
|
||||
case DartEngine_SnapshotKind_AOT:
|
||||
// No need to free AOT buffers loaded from dynamic library.
|
||||
break;
|
||||
default:
|
||||
Syslog::PrintErr("Unsupported SnapshotKind: %d\n", snapshot.kind);
|
||||
break;
|
||||
}
|
||||
free(const_cast<char*>(snapshot.script_uri));
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::HandleMessage(Dart_Isolate isolate) {
|
||||
LockIsolate(isolate);
|
||||
Dart_EnterIsolate(isolate);
|
||||
Dart_EnterScope();
|
||||
|
||||
Dart_Handle handle_result = Dart_HandleMessage();
|
||||
|
||||
if (Dart_IsError(handle_result)) {
|
||||
if (handle_message_error_callback_ != nullptr) {
|
||||
handle_message_error_callback_(handle_result, isolate);
|
||||
} else {
|
||||
Syslog::PrintErr("Error handling isolate message: %s",
|
||||
Dart_GetError(handle_result));
|
||||
}
|
||||
}
|
||||
|
||||
Dart_ExitScope();
|
||||
Dart_ExitIsolate();
|
||||
UnlockIsolate(isolate);
|
||||
}
|
||||
|
||||
Mutex& Engine::MutexForIsolate(Dart_Isolate isolate) {
|
||||
MutexLocker ml(&engine_state_);
|
||||
return mutexes_[isolate];
|
||||
}
|
||||
|
||||
void Engine::LockIsolate(Dart_Isolate isolate) {
|
||||
MutexForIsolate(isolate).Lock();
|
||||
}
|
||||
|
||||
void Engine::UnlockIsolate(Dart_Isolate isolate) {
|
||||
MutexForIsolate(isolate).Unlock();
|
||||
}
|
||||
|
||||
void Engine::NotifyMessage(Dart_Isolate isolate) {
|
||||
if (!engine_lifecycle_.TryLock() || !is_running_) {
|
||||
// Shutdown is in progress or complete.
|
||||
//
|
||||
// Used to prevent a potential deadlock during shutdown.
|
||||
//
|
||||
// If Engine::MessageNotifyCallback is waiting for Engine::HandleMessage
|
||||
// completion (which happens in samples/embedder/run_timer_async), and
|
||||
// another thread calls Engine::Shutdown, the deadlock may occur:
|
||||
//
|
||||
// 1. MessageNotifyCallback thread owns PortMap::mutex_ (through
|
||||
// PortMap::PostMessage) and wants to lock an isolate (via
|
||||
// Engine::LockIsolate).
|
||||
// 2. Shutdown thread owns an isolate lock and wants to lock PortMap::mutex_
|
||||
// (inside Dart_ShutdownIsolate call).
|
||||
//
|
||||
// This mutex is used to prevent it:
|
||||
// - Engine::Shutdown locks it.
|
||||
// - Engine::NotifyMessage tries to lock it, and treats lock failure as
|
||||
// shutdown in progress.
|
||||
return;
|
||||
}
|
||||
|
||||
DartEngine_MessageScheduler scheduler;
|
||||
{
|
||||
MutexLocker ml(&engine_state_);
|
||||
scheduler = schedulers_[isolate];
|
||||
}
|
||||
|
||||
if (scheduler.schedule_callback == nullptr) {
|
||||
scheduler = default_scheduler_;
|
||||
}
|
||||
if (scheduler.schedule_callback == nullptr) {
|
||||
engine_lifecycle_.Unlock();
|
||||
return;
|
||||
}
|
||||
scheduler.schedule_callback(isolate, scheduler.context);
|
||||
engine_lifecycle_.Unlock();
|
||||
}
|
||||
|
||||
void Engine::SetHandleMessageErrorCallback(
|
||||
DartEngine_HandleMessageErrorCallback callback) {
|
||||
handle_message_error_callback_ = callback;
|
||||
}
|
||||
|
||||
void Engine::SetDefaultMessageScheduler(DartEngine_MessageScheduler scheduler) {
|
||||
default_scheduler_ = scheduler;
|
||||
}
|
||||
|
||||
void Engine::SetMessageScheduler(DartEngine_MessageScheduler scheduler,
|
||||
Dart_Isolate isolate) {
|
||||
MutexLocker ml(&engine_state_);
|
||||
schedulers_[isolate] = scheduler;
|
||||
}
|
||||
|
||||
} // namespace engine
|
||||
} // namespace dart
|
||||
@@ -0,0 +1,134 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
#ifndef RUNTIME_ENGINE_ENGINE_H_
|
||||
#define RUNTIME_ENGINE_ENGINE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include "include/dart_engine.h"
|
||||
#include "platform/synchronization.h"
|
||||
|
||||
namespace dart {
|
||||
namespace engine {
|
||||
|
||||
// Manages Dart VM initialization/shutdown, isolates, and all state that needs
|
||||
// to be disposed (e.g. buffers of loaded snapshots).
|
||||
//
|
||||
// Since Dart VM has a global state, there should be only one instance of this
|
||||
// class (accessible through Engine::instance()).
|
||||
class Engine {
|
||||
public:
|
||||
// Loads Kernel snapshot from given file path.
|
||||
//
|
||||
// Just reads all bytes into a single buffer, owned by this class.
|
||||
DartEngine_SnapshotData KernelFromFile(const char* path, char** error);
|
||||
|
||||
// Loads AOT snapshot from given file path.
|
||||
//
|
||||
// Opens given AOT snapshot as a shared library and loads buffer symbols from
|
||||
// it.
|
||||
DartEngine_SnapshotData AotFromFile(const char* path, char** error);
|
||||
|
||||
// Starts an isolate from a snapshot.
|
||||
//
|
||||
// Starting an isolate also routes message notify callback to NotifyMessage.
|
||||
Dart_Isolate StartIsolate(const DartEngine_SnapshotData snapshot,
|
||||
char** error);
|
||||
|
||||
// Acquires a lock for an isolate, so that the thread can enter it.
|
||||
void LockIsolate(Dart_Isolate isolate);
|
||||
|
||||
// Releases the lock obtained by LockIsolate.
|
||||
void UnlockIsolate(Dart_Isolate isolate);
|
||||
|
||||
// Called by Dart VM via Engine::MessageNotifyCallback.
|
||||
//
|
||||
// Calls isolate-specific message scheduler (if any), default message
|
||||
// scheduler, or ignores the message.
|
||||
void NotifyMessage(Dart_Isolate isolate);
|
||||
|
||||
// Calls Dart_HandleMessage, managing an isolate lock and Dart scope.
|
||||
void HandleMessage(Dart_Isolate isolate);
|
||||
|
||||
// Sets a callback to be called when Dart_HandleMessage returns an error.
|
||||
void SetHandleMessageErrorCallback(
|
||||
DartEngine_HandleMessageErrorCallback callback);
|
||||
|
||||
// Sets a message scheduler for a given isolate.
|
||||
void SetDefaultMessageScheduler(DartEngine_MessageScheduler scheduler);
|
||||
|
||||
// Sets default message scheduler for isolates without dedicated
|
||||
// message scheduler.
|
||||
void SetMessageScheduler(DartEngine_MessageScheduler scheduler,
|
||||
Dart_Isolate isolate);
|
||||
|
||||
// Initializes embedder and partially initializes Dart VM.
|
||||
//
|
||||
// Full initialization happens only when the user starts the first isolate, as
|
||||
// in case of AOT snapshots we don't have vm snapshot data and vm snapshot
|
||||
// instuructions before the user makes the first call to StartIsolate.
|
||||
bool Initialize(char** error);
|
||||
|
||||
// Shuts down the engine.
|
||||
//
|
||||
// Once shut down, it is not supposed to be re-initialized.
|
||||
void Shutdown();
|
||||
|
||||
static Engine* instance();
|
||||
// Passed to Dart_SetMessageNotifyCallback.
|
||||
static void MessageNotifyCallback(Dart_Isolate isolate);
|
||||
// Passed to user-previded message scheduler callback.
|
||||
static void HandleMessageCallback(Dart_Isolate isolate);
|
||||
|
||||
private:
|
||||
// Set to false once shutdown starts.
|
||||
bool is_running_ = false;
|
||||
|
||||
// Controls initalization and shutdown, and prevents deadlocks when delivering
|
||||
// messages during shutdown.
|
||||
Mutex engine_lifecycle_;
|
||||
|
||||
// Guards access to engine state.
|
||||
Mutex engine_state_;
|
||||
|
||||
// Whether Engine::Initialize is called.
|
||||
bool initialized_ = false;
|
||||
|
||||
// Added because of AOT mode, because in AOT mode Dart VM cannot be fully
|
||||
// initialized until we get vm snapshot data, which we typically get when the
|
||||
// user starts the first isolate.
|
||||
bool first_isolate_started_ = false;
|
||||
|
||||
// Snapshots, whose buffers are owned by the engine.
|
||||
// and are freed on Shutdown.
|
||||
std::vector<DartEngine_SnapshotData> owned_snapshots_;
|
||||
|
||||
// Loaded dynamic libraries. Dynamic libraries are loaded
|
||||
// when reading AOT snapshots.
|
||||
std::vector<void*> loaded_libraries_;
|
||||
|
||||
// All isolates, started via Engine::StartIsolate.
|
||||
std::vector<Dart_Isolate> isolates_;
|
||||
|
||||
// Stores per-isolate mutexes, used by Engine::LockIsolate/UnlockIsolate.
|
||||
std::unordered_map<Dart_Isolate, Mutex> mutexes_;
|
||||
|
||||
// Default scheduler.
|
||||
DartEngine_MessageScheduler default_scheduler_;
|
||||
// Per-isolate message schedulers.
|
||||
std::unordered_map<Dart_Isolate, DartEngine_MessageScheduler> schedulers_;
|
||||
|
||||
// Callback to notify about Dart_HandleMessage errors.
|
||||
DartEngine_HandleMessageErrorCallback handle_message_error_callback_;
|
||||
|
||||
// Helper function to get an element from mutexes_.
|
||||
Mutex& MutexForIsolate(Dart_Isolate isolate);
|
||||
};
|
||||
|
||||
} // namespace engine
|
||||
} // namespace dart
|
||||
#endif // RUNTIME_ENGINE_ENGINE_H_
|
||||
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2025, 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.
|
||||
|
||||
#ifndef RUNTIME_ENGINE_INCLUDE_DART_ENGINE_H_
|
||||
#define RUNTIME_ENGINE_INCLUDE_DART_ENGINE_H_
|
||||
#include "include/dart_api.h"
|
||||
|
||||
/**
|
||||
* Initializes the Dart engine.
|
||||
*
|
||||
* \param[out] error Set to NULL if initialization was successful.
|
||||
* Otherwise is set to a description of error which occurred during
|
||||
* initialization. The caller is responsible for freeing the error string.
|
||||
*
|
||||
* \return true if initialization is successful and false otherwise.
|
||||
*/
|
||||
DART_EXPORT bool DartEngine_Init(char** error);
|
||||
|
||||
/**
|
||||
* Shuts down the Dart engine.
|
||||
*
|
||||
* Stops all isolates and frees all resources.
|
||||
*/
|
||||
DART_EXPORT void DartEngine_Shutdown();
|
||||
|
||||
/**
|
||||
* Kind of a snapshot represented by DartEngine_SnapshotData.
|
||||
*/
|
||||
typedef enum {
|
||||
DartEngine_SnapshotKind_Kernel,
|
||||
DartEngine_SnapshotKind_AOT,
|
||||
} DartEngine_SnapshotKind;
|
||||
|
||||
/**
|
||||
* Dart snapshot data, sufficient to create an isolate.
|
||||
*/
|
||||
typedef struct DartEngine_SnapshotData {
|
||||
/**
|
||||
* Uniquely identifies a snapshot.
|
||||
*
|
||||
* It is passed to \ref Dart_CreateIsolateGroup or
|
||||
* \ref Dart_CreateIsolateGroupFromKernel.
|
||||
*/
|
||||
const char* script_uri;
|
||||
/**
|
||||
* Whether this snapshot data reprents Kernel or AOT snapshot.
|
||||
*/
|
||||
DartEngine_SnapshotKind kind;
|
||||
union {
|
||||
struct {
|
||||
const uint8_t* kernel_buffer;
|
||||
intptr_t kernel_buffer_size;
|
||||
};
|
||||
struct {
|
||||
const uint8_t* vm_snapshot_data;
|
||||
const uint8_t* vm_snapshot_instructions;
|
||||
const uint8_t* vm_isolate_data;
|
||||
const uint8_t* vm_isolate_instructions;
|
||||
};
|
||||
};
|
||||
} DartEngine_SnapshotData;
|
||||
|
||||
/**
|
||||
* Creates a new isolate for a given snapshot.
|
||||
*
|
||||
* \param snapshot_data Snapshot to start an isolate from.
|
||||
* \param[out] error Set to NULL if an isolate is created successfully.
|
||||
* Otherwise is set to a description of error which occurred during
|
||||
* isolate creation. The caller is responsible for freeing the error string.
|
||||
*
|
||||
* \return The new isolate on success, or NULL if isolate creation failed.
|
||||
*/
|
||||
DART_EXPORT Dart_Isolate
|
||||
DartEngine_CreateIsolate(DartEngine_SnapshotData snapshot_data, char** error);
|
||||
|
||||
/**
|
||||
* Blocks until the isolate is available for entering and enters an isolate.
|
||||
*
|
||||
* This is an alternative to \ref Dart_EnterIsolate, which can be used when
|
||||
* multiple threads may try to enter the same isolate at the same time.
|
||||
*
|
||||
* Use \ref DartEngine_ReleaseIsolate instead of `Dart_ExitIsolate` if you
|
||||
* use this function.
|
||||
*/
|
||||
DART_EXPORT void DartEngine_AcquireIsolate(Dart_Isolate isolate);
|
||||
|
||||
/**
|
||||
* Exits an isolate and makes it available for entering by other threads.
|
||||
*
|
||||
* This is an alternative to \ref Dart_ExitIsolate, which should be used
|
||||
* if you use \ref DartEngine_AcquireIsolate.
|
||||
*/
|
||||
DART_EXPORT void DartEngine_ReleaseIsolate();
|
||||
|
||||
/**
|
||||
* A callback invoked when there's an error handling isolate message.
|
||||
*
|
||||
* \param error Dart error happened during handling a message.
|
||||
* \param destination_isolate The isolate where the error occurred.
|
||||
*/
|
||||
typedef void (*DartEngine_HandleMessageErrorCallback)(
|
||||
Dart_Handle error,
|
||||
Dart_Isolate destination_isolate);
|
||||
|
||||
/**
|
||||
* Sets the callback for errors during handling isolate messages.
|
||||
*/
|
||||
DART_EXPORT void DartEngine_SetHandleMessageErrorCallback(
|
||||
DartEngine_HandleMessageErrorCallback handle_message_error_callback);
|
||||
|
||||
/**
|
||||
* Handles a single message for an isolate.
|
||||
*/
|
||||
DART_EXPORT void DartEngine_HandleMessage(Dart_Isolate isolate);
|
||||
|
||||
/**
|
||||
* Message scheduling callback.
|
||||
*
|
||||
* Dart Engine calls this callback whenever there's a new message for an
|
||||
* isolate, and the callback should schedule an execution of
|
||||
* DartEngine_HandleMessage with given isolate.
|
||||
*
|
||||
* \param isolate Destination isolate for a mesage.
|
||||
* \param context Context from \ref DartEngine_MessageScheduler.
|
||||
*/
|
||||
typedef void (*DartEngine_ScheduleMessageCallback)(Dart_Isolate isolate,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Scheduler for isolate messages.
|
||||
*
|
||||
* Whenever Dart isolate has a new message, the Dart engine will
|
||||
* call schedule_callback and pass context into it.
|
||||
*/
|
||||
typedef struct DartEngine_MessageScheduler {
|
||||
DartEngine_ScheduleMessageCallback schedule_callback;
|
||||
void* context;
|
||||
} DartEngine_MessageScheduler;
|
||||
|
||||
/**
|
||||
* Sets default message scheduler for all isolates.
|
||||
*/
|
||||
DART_EXPORT void DartEngine_SetDefaultMessageScheduler(
|
||||
DartEngine_MessageScheduler scheduler);
|
||||
|
||||
/**
|
||||
* Sets message scheduler for isolate.
|
||||
*/
|
||||
DART_EXPORT void DartEngine_SetMessageScheduler(
|
||||
DartEngine_MessageScheduler scheduler,
|
||||
Dart_Isolate isolate);
|
||||
|
||||
/**
|
||||
* Loads \ref DartEngine_SnapshotData from file
|
||||
*
|
||||
* User should not free uri and buffer.
|
||||
*
|
||||
* \param[out] error Set to NULL if snapshot could be loaded successfully.
|
||||
* Otherwise is set to a description of error which occurred during
|
||||
* snapshot loading. The caller is responsible for freeing the error string.
|
||||
*
|
||||
* \return Snapshot data with kind set to \ref DartEngine_SnapshotKind_Kernel.
|
||||
*/
|
||||
DART_EXPORT DartEngine_SnapshotData DartEngine_KernelFromFile(const char* path,
|
||||
char** error);
|
||||
/**
|
||||
* Loads \ref DartEngine_SnapshotData from file
|
||||
*
|
||||
* User should not free uri and buffers.
|
||||
*
|
||||
* \param[out] error Set to NULL if snapshot could be loaded successfully.
|
||||
* Otherwise is set to a description of error which occurred during
|
||||
* snapshot loading. The caller is responsible for freeing the error string.
|
||||
*
|
||||
* \return Snapshot data with kind set to \ref DartEngine_SnapshotKind_AOT.
|
||||
*/
|
||||
DART_EXPORT DartEngine_SnapshotData
|
||||
DartEngine_AotSnapshotFromFile(const char* path, char** error);
|
||||
|
||||
#endif // RUNTIME_ENGINE_INCLUDE_DART_ENGINE_H_
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.cs.allow-jit</key>
|
||||
<true/>
|
||||
<key>com.apple.security.cs.disable-library-validation</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -46,6 +46,8 @@ List<String> compilerFlagsForFile(String filepath) {
|
||||
'-Iruntime',
|
||||
'-Ithird_party',
|
||||
'-Iruntime/include',
|
||||
if (filepath.contains('samples/embedder'))
|
||||
'-Iruntime/engine',
|
||||
'-Ithird_party/boringssl/src/include',
|
||||
'-Ithird_party/perfetto/include',
|
||||
'-Ithird_party/zlib',
|
||||
|
||||
+129
-10
@@ -2,24 +2,143 @@
|
||||
# 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.
|
||||
|
||||
import("../../utils/aot_snapshot.gni")
|
||||
import("../../utils/application_snapshot.gni")
|
||||
|
||||
# All samples.
|
||||
group("all") {
|
||||
deps = [ ":run_kernel" ]
|
||||
deps = [
|
||||
":aot",
|
||||
":kernel",
|
||||
]
|
||||
}
|
||||
|
||||
group("aot") {
|
||||
deps = [
|
||||
":run_main_aot",
|
||||
":run_timer_aot",
|
||||
":run_timer_async_aot",
|
||||
":run_two_programs_aot",
|
||||
]
|
||||
}
|
||||
|
||||
group("kernel") {
|
||||
deps = [
|
||||
":run_main_kernel",
|
||||
":run_timer_async_kernel",
|
||||
":run_timer_kernel",
|
||||
":run_two_programs_kernel",
|
||||
]
|
||||
}
|
||||
|
||||
# Generates a pair of executables for kernel and AOT snapshots.
|
||||
template("sample") {
|
||||
executable("${target_name}_kernel") {
|
||||
# Otherwise build with --no-clang fails.
|
||||
if (is_linux) {
|
||||
ldflags = [ "-Wl,--allow-shlib-undefined" ]
|
||||
}
|
||||
include_dirs = [
|
||||
"../../runtime",
|
||||
"../../runtime/engine",
|
||||
]
|
||||
deps = [ "../../runtime/engine:dart_engine_jit_shared" ]
|
||||
if (defined(invoker.deps)) {
|
||||
deps += invoker.deps
|
||||
}
|
||||
data_deps = []
|
||||
foreach(snapshot, invoker.snapshots) {
|
||||
data_deps += [ "${snapshot}_kernel" ]
|
||||
}
|
||||
forward_variables_from(invoker,
|
||||
"*",
|
||||
[
|
||||
"snapshots",
|
||||
"deps",
|
||||
"data_deps",
|
||||
])
|
||||
}
|
||||
|
||||
executable("${target_name}_aot") {
|
||||
# Otherwise build with MSAN fails.
|
||||
if (is_linux) {
|
||||
ldflags = [ "-Wl,--allow-shlib-undefined" ]
|
||||
}
|
||||
include_dirs = [
|
||||
"../../runtime",
|
||||
"../../runtime/engine",
|
||||
]
|
||||
deps = [ "../../runtime/engine:dart_engine_aot_shared" ]
|
||||
if (defined(invoker.deps)) {
|
||||
deps += invoker.deps
|
||||
}
|
||||
data_deps = []
|
||||
foreach(snapshot, invoker.snapshots) {
|
||||
data_deps += [ "${snapshot}_aot" ]
|
||||
}
|
||||
forward_variables_from(invoker,
|
||||
"*",
|
||||
[
|
||||
"snapshots",
|
||||
"deps",
|
||||
"data_deps",
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
# For a given main_dart generates Kernel and AOT snapshots
|
||||
template("snapshots") {
|
||||
# Kernel snapshot
|
||||
application_snapshot("${target_name}_kernel") {
|
||||
main_dart = invoker.main_dart
|
||||
dart_snapshot_kind = "kernel"
|
||||
training_args = [] # Not used
|
||||
gen_kernel_args = [ "--link-platform" ]
|
||||
}
|
||||
|
||||
# AOT snapshot
|
||||
aot_snapshot("${target_name}_aot") {
|
||||
main_dart = invoker.main_dart
|
||||
}
|
||||
}
|
||||
|
||||
# Sample binary to run given kernel snapshot.
|
||||
executable("run_kernel") {
|
||||
sources = [ "run_kernel.cc" ]
|
||||
deps = [ "../../runtime/bin:dart_embedder_runtime_jit" ]
|
||||
include_dirs = [ "../../runtime" ]
|
||||
data_deps = [ ":hello_kernel" ]
|
||||
sample("run_main") {
|
||||
sources = [ "run_main.cc" ]
|
||||
snapshots = [ ":hello" ]
|
||||
}
|
||||
|
||||
# Kernel snapshot of ./hello.dart.
|
||||
application_snapshot("hello_kernel") {
|
||||
snapshots("hello") {
|
||||
main_dart = "hello.dart"
|
||||
dart_snapshot_kind = "kernel"
|
||||
training_args = [] # Not used
|
||||
}
|
||||
|
||||
# Sample binary to run two snapshots simultaneously.
|
||||
sample("run_two_programs") {
|
||||
sources = [ "run_two_programs.cc" ]
|
||||
snapshots = [
|
||||
":program1",
|
||||
":program2",
|
||||
]
|
||||
}
|
||||
|
||||
snapshots("program1") {
|
||||
main_dart = "program1.dart"
|
||||
}
|
||||
|
||||
snapshots("program2") {
|
||||
main_dart = "program2.dart"
|
||||
}
|
||||
|
||||
sample("run_timer") {
|
||||
sources = [ "run_timer.cc" ]
|
||||
snapshots = [ ":timer" ]
|
||||
}
|
||||
|
||||
sample("run_timer_async") {
|
||||
sources = [ "run_timer_async.cc" ]
|
||||
snapshots = [ ":timer" ]
|
||||
}
|
||||
|
||||
snapshots("timer") {
|
||||
main_dart = "timer.dart"
|
||||
}
|
||||
|
||||
+31
-14
@@ -2,26 +2,43 @@
|
||||
|
||||
Examples of using Dart VM and executing Dart code from C++ binaries.
|
||||
|
||||
## run_kernel.cc
|
||||
All examples can run either AOT or Kernel snapshots, depending on
|
||||
which shared library variant they depend on.
|
||||
|
||||
To run the example:
|
||||
Since snapshot file formats are unstable, the `dart` binary needs to
|
||||
be of a matching version. The simplest way to ensure this is to build
|
||||
Dart SDK from the same checkout, see [Building Dart
|
||||
SDK](https://github.com/dart-lang/sdk/blob/main/docs/Building.md#building).
|
||||
|
||||
## `run_main.cc`
|
||||
|
||||
This is the simplest example, which just calls a `main` function from a given AOT/Kernel
|
||||
snapshot. It does not handle isolate messages, so it cannot run Dart
|
||||
programs with async functions.
|
||||
|
||||
To run the example with a Kernel snapshot:
|
||||
|
||||
```sh
|
||||
./tools/build.py --no-rbe --mode=release samples/embedder:run_kernel && out/ReleaseX64/run_kernel
|
||||
./tools/build.py --mode=release samples/embedder:run_main_kernel && \
|
||||
out/ReleaseX64/run_main_kernel out/ReleaseX64/gen/hello_kernel.dart.snapshot.
|
||||
```
|
||||
|
||||
The example initializes Dart VM, creates an isolate from a kernel file (by
|
||||
default it uses kernel-compiled `hello.dart`), launches its `main` function with
|
||||
args and exits.
|
||||
|
||||
You can also compile your own Dart kernel like this:
|
||||
To run the example with an AOT snapshot:
|
||||
|
||||
```sh
|
||||
dart compile kernel --no-link-platform my.dart
|
||||
out/ReleaseX64/run_kernel my.dill
|
||||
./tools/build.py --mode=release samples/embedder:run_main_aot && \
|
||||
out/ReleaseX64/run_main_aot out/ReleaseX64/hello_aot.snapshot.
|
||||
```
|
||||
|
||||
Since the kernel file format is unstable, the `dart` binary needs to be of a
|
||||
matching version. The simplest way to ensure this is to build Dart SDK from the
|
||||
same checkout, see
|
||||
[Building Dart SDK](https://github.com/dart-lang/sdk/blob/main/docs/Building.md#building).
|
||||
## `run_two_programs.cc`
|
||||
|
||||
This example calls a function from one Dart snapshot and then passes
|
||||
the returned string to another Dart snapshot.
|
||||
|
||||
## `run_timer.cc`
|
||||
|
||||
Demonstrates running an isolate event loop in a separate thread.
|
||||
|
||||
## `run_timer_async.cc`
|
||||
|
||||
Demonstrates a custom message scheduler using `std::async`.
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
// 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.
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
void main(List<String>? args) {
|
||||
final greetee = args?.singleOrNull ?? 'world';
|
||||
print('Hello, $greetee!');
|
||||
@pragma('vm:entry-point', 'call')
|
||||
void main(List<String> args) {
|
||||
greet(args[0]);
|
||||
}
|
||||
|
||||
void greet(String person) {
|
||||
print("hi, $person!");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2025, 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.
|
||||
*/
|
||||
|
||||
#ifndef SAMPLES_EMBEDDER_HELPERS_H_
|
||||
#define SAMPLES_EMBEDDER_HELPERS_H_
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_engine.h"
|
||||
|
||||
// Loads kernel/AOT snapshot from path depending on
|
||||
// whether we use precompiled runtime.
|
||||
inline DartEngine_SnapshotData AutoSnapshotFromFile(std::string_view path,
|
||||
char** error) {
|
||||
std::string path_string(path);
|
||||
if (Dart_IsPrecompiledRuntime()) {
|
||||
return DartEngine_AotSnapshotFromFile(path_string.c_str(), error);
|
||||
} else {
|
||||
return DartEngine_KernelFromFile(path_string.c_str(), error);
|
||||
}
|
||||
}
|
||||
|
||||
inline void CheckError(char* error, std::string_view context = "") {
|
||||
if (error != nullptr) {
|
||||
std::cerr << "Error " << context << ": " << error << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
inline Dart_Handle CheckError(Dart_Handle handle,
|
||||
std::string_view context = "") {
|
||||
if (Dart_IsError(handle)) {
|
||||
std::cerr << "Error " << context << ": " << Dart_GetError(handle)
|
||||
<< std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
inline std::string StringFromHandle(Dart_Handle handle) {
|
||||
CheckError(handle, "StringFromHandle received an error");
|
||||
|
||||
if (!Dart_IsString(handle)) {
|
||||
std::cerr << "StringFromHandle handle is not a string" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
const char* return_value_tmp;
|
||||
Dart_Handle to_string_result =
|
||||
Dart_StringToCString(handle, &return_value_tmp);
|
||||
CheckError(to_string_result, "Dart_StringToCString");
|
||||
|
||||
return std::string(return_value_tmp);
|
||||
}
|
||||
|
||||
inline int64_t IntFromHandle(Dart_Handle handle) {
|
||||
CheckError(handle, "IntFromHandle received an error");
|
||||
|
||||
if (!Dart_IsInteger(handle)) {
|
||||
std::cerr << "IntFromHandle handle is not an int" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
int64_t result;
|
||||
Dart_Handle to_int64_result = Dart_IntegerToInt64(handle, &result);
|
||||
CheckError(to_int64_result, "Dart_IntegerToInt64");
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline T WithIsolate(Dart_Isolate isolate, std::function<T()> body) {
|
||||
DartEngine_AcquireIsolate(isolate);
|
||||
Dart_EnterScope();
|
||||
T result = body();
|
||||
Dart_ExitScope();
|
||||
DartEngine_ReleaseIsolate();
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void WithIsolate(Dart_Isolate isolate, std::function<void()> body) {
|
||||
DartEngine_AcquireIsolate(isolate);
|
||||
Dart_EnterScope();
|
||||
body();
|
||||
Dart_ExitScope();
|
||||
DartEngine_ReleaseIsolate();
|
||||
}
|
||||
|
||||
#endif /* SAMPLES_EMBEDDER_HELPERS_H_ */
|
||||
@@ -0,0 +1,8 @@
|
||||
void main(List<String> args) {
|
||||
throw 'Unimplemented';
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point', 'call')
|
||||
String getValue() {
|
||||
return 'program1';
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
void main(List<String> args) {
|
||||
throw 'Unimplemented';
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point', 'call')
|
||||
void printValue(String value) {
|
||||
print('program2 received: $value');
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
// Copyright (c) 2024, 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.
|
||||
|
||||
// Executes `main` function from given Dart kernel binary (by default uses
|
||||
// compiled ./hello.dart).
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include "bin/dartutils.h"
|
||||
#include "bin/dfe.h"
|
||||
#include "bin/platform.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_embedder_api.h"
|
||||
#include "platform/assert.h"
|
||||
|
||||
Dart_Handle CheckHandle(Dart_Handle handle,
|
||||
const char* context = "unknown context") {
|
||||
if (Dart_IsError(handle)) {
|
||||
FATAL("Dart error (%s): %s", context, Dart_GetError(handle));
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
void CheckError(bool condition, const char* error, const char* context) {
|
||||
if (!condition) {
|
||||
FATAL("Dart error (%s): %s", context, error);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckError(const char* error, const char* context) {
|
||||
if (error != nullptr) {
|
||||
FATAL("Dart error (%s): %s", context, error);
|
||||
}
|
||||
}
|
||||
|
||||
Dart_InitializeParams CreateInitializeParams() {
|
||||
Dart_InitializeParams params;
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
|
||||
return params;
|
||||
}
|
||||
|
||||
std::string GetExecutablePath() {
|
||||
const size_t kPathBufSize = PATH_MAX + 1;
|
||||
char executable_path[kPathBufSize] = {};
|
||||
|
||||
intptr_t path_length = dart::bin::Platform::ResolveExecutablePathInto(
|
||||
executable_path, kPathBufSize);
|
||||
CheckError(path_length > 0, "empty executable path",
|
||||
"ResolveExecutablePathInfo");
|
||||
return std::string(executable_path, path_length);
|
||||
}
|
||||
|
||||
std::string GetDefaultSnapshotPath() {
|
||||
std::string executable_path = GetExecutablePath();
|
||||
std::string directory =
|
||||
executable_path.substr(0, executable_path.find_last_of("/\\"));
|
||||
return directory + "/gen/hello_kernel.dart.snapshot";
|
||||
}
|
||||
|
||||
std::string ReadSnapshot(std::string_view path) {
|
||||
std::string path_string{path};
|
||||
std::ifstream source_file{path_string, std::ios::binary};
|
||||
|
||||
ASSERT(source_file.good());
|
||||
source_file.seekg(0, source_file.end);
|
||||
uint64_t length = source_file.tellg();
|
||||
source_file.seekg(0, source_file.beg);
|
||||
|
||||
char* bytes = static_cast<char*>(std::malloc(length));
|
||||
source_file.read(bytes, length);
|
||||
auto result = std::string(bytes, length);
|
||||
std::free(bytes);
|
||||
return result;
|
||||
}
|
||||
|
||||
Dart_Handle ToDartStringList(const std::vector<std::string>& values) {
|
||||
Dart_Handle string_type =
|
||||
CheckHandle(dart::bin::DartUtils::GetDartType("dart:core", "String"));
|
||||
Dart_Handle filler = CheckHandle(Dart_NewStringFromCString(""));
|
||||
|
||||
Dart_Handle result =
|
||||
CheckHandle(Dart_NewListOfTypeFilled(string_type, filler, values.size()));
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
Dart_Handle element =
|
||||
CheckHandle(Dart_NewStringFromCString(values[i].c_str()));
|
||||
CheckHandle(Dart_ListSetAt(result, i, element));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::string snapshot_path =
|
||||
argc == 1 ? GetDefaultSnapshotPath() : std::string(argv[1]);
|
||||
|
||||
std::string snapshot_name =
|
||||
snapshot_path.substr(snapshot_path.find_last_of("/\\") + 1);
|
||||
std::string snapshot_data = ReadSnapshot(snapshot_path);
|
||||
std::string snapshot_uri = "file://" + snapshot_path;
|
||||
std::cout << "Snapshot path: " << snapshot_path << std::endl;
|
||||
char* error;
|
||||
|
||||
// Start Dart VM.
|
||||
bool result = dart::embedder::InitOnce(&error);
|
||||
CheckError(result, error, "dart::embedder::InitOnce");
|
||||
|
||||
std::vector<const char*> flags{};
|
||||
CheckError(Dart_SetVMFlags(flags.size(), flags.data()), "Dart_SetVMFlags");
|
||||
|
||||
Dart_InitializeParams initialize_params = CreateInitializeParams();
|
||||
CheckError(Dart_Initialize(&initialize_params), "Dart_Initialize");
|
||||
|
||||
dart::bin::DFE dfe;
|
||||
dfe.Init();
|
||||
const uint8_t* platform_buffer = nullptr;
|
||||
intptr_t platform_buffer_size = 0;
|
||||
|
||||
dfe.LoadPlatform(&platform_buffer, &platform_buffer_size);
|
||||
|
||||
// Start an isolate from a platform kernel.
|
||||
Dart_IsolateFlags isolate_flags;
|
||||
Dart_IsolateFlagsInitialize(&isolate_flags);
|
||||
|
||||
Dart_CreateIsolateGroupFromKernel(
|
||||
/*script_uri=*/snapshot_uri.c_str(),
|
||||
/*name=*/snapshot_name.c_str(),
|
||||
/*kernel_buffer=*/platform_buffer,
|
||||
/*kernel_buffer_size=*/platform_buffer_size,
|
||||
/*flags=*/&isolate_flags,
|
||||
/*isolate_group_data=*/nullptr,
|
||||
/*isolate_data=*/nullptr, &error);
|
||||
CheckError(error, "Dart_CreateIsolateGroupFromKernel");
|
||||
Dart_EnterScope();
|
||||
CheckHandle(dart::bin::DartUtils::PrepareForScriptLoading(
|
||||
/*is_service_isolate=*/false, /*trace_loading=*/false),
|
||||
"PrepareForScriptLoading");
|
||||
|
||||
// Load kernel snapshot to run `main` from.
|
||||
Dart_Handle library =
|
||||
CheckHandle(Dart_LoadLibraryFromKernel(
|
||||
reinterpret_cast<const uint8_t*>(snapshot_data.c_str()),
|
||||
snapshot_data.size()),
|
||||
"Dart_LoadLibraryFromKernel");
|
||||
|
||||
// Call main function with args.
|
||||
std::initializer_list<Dart_Handle> main_args{ToDartStringList({"universe"})};
|
||||
CheckHandle(Dart_Invoke(library, Dart_NewStringFromCString("main"), 1,
|
||||
const_cast<Dart_Handle*>(main_args.begin())),
|
||||
"Dart_Invoke('main')");
|
||||
Dart_ExitScope();
|
||||
Dart_ShutdownIsolate();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) 2025, 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 <iostream>
|
||||
#include "helpers.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_engine.h"
|
||||
|
||||
Dart_Handle ToDartStringList(const std::vector<std::string>& values) {
|
||||
Dart_Handle core_library =
|
||||
CheckError(Dart_LookupLibrary(Dart_NewStringFromCString("dart:core")));
|
||||
Dart_Handle string_type = CheckError(Dart_GetNonNullableType(
|
||||
core_library, Dart_NewStringFromCString("String"), 0, nullptr));
|
||||
Dart_Handle filler = Dart_NewStringFromCString("");
|
||||
|
||||
Dart_Handle result =
|
||||
CheckError(Dart_NewListOfTypeFilled(string_type, filler, values.size()));
|
||||
for (size_t i = 0; i < values.size(); i++) {
|
||||
Dart_Handle element = Dart_NewStringFromCString(values[i].c_str());
|
||||
CheckError(Dart_ListSetAt(result, i, element));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc == 1) {
|
||||
std::cerr << "Must specify snapshot path" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
char* error = nullptr;
|
||||
|
||||
DartEngine_SnapshotData snapshot_data = AutoSnapshotFromFile(argv[1], &error);
|
||||
CheckError(error, "reading snapshot");
|
||||
|
||||
Dart_Isolate isolate = DartEngine_CreateIsolate(snapshot_data, &error);
|
||||
CheckError(error, "starting isolate");
|
||||
|
||||
DartEngine_AcquireIsolate(isolate);
|
||||
Dart_EnterScope();
|
||||
|
||||
std::initializer_list<Dart_Handle> main_args{ToDartStringList({"world"})};
|
||||
|
||||
CheckError(Dart_Invoke(Dart_RootLibrary(), Dart_NewStringFromCString("main"),
|
||||
1, const_cast<Dart_Handle*>(main_args.begin())),
|
||||
"calling main");
|
||||
|
||||
Dart_ExitScope();
|
||||
DartEngine_ReleaseIsolate();
|
||||
|
||||
DartEngine_Shutdown();
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <condition_variable>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include "helpers.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_engine.h"
|
||||
|
||||
// Calls `startTimer` from timer.dart
|
||||
void StartTimer(Dart_Isolate isolate, uint32_t millis) {
|
||||
WithIsolate(isolate, [&]() {
|
||||
std::initializer_list<Dart_Handle> args{Dart_NewInteger(millis)};
|
||||
CheckError(
|
||||
Dart_Invoke(Dart_RootLibrary(), Dart_NewStringFromCString("startTimer"),
|
||||
1, const_cast<Dart_Handle*>(args.begin())),
|
||||
"calling startTimer");
|
||||
});
|
||||
}
|
||||
|
||||
// Calls `stopTimer` from timer.dart
|
||||
void StopTimer(Dart_Isolate isolate) {
|
||||
WithIsolate(isolate, [&]() {
|
||||
CheckError(Dart_Invoke(Dart_RootLibrary(),
|
||||
Dart_NewStringFromCString("stopTimer"), 0, nullptr),
|
||||
"calling stopTimer");
|
||||
});
|
||||
}
|
||||
|
||||
// Gets `ticks` from timer.dart
|
||||
int64_t GetTicks(Dart_Isolate isolate) {
|
||||
return WithIsolate<int64_t>(isolate, []() {
|
||||
return IntFromHandle(
|
||||
Dart_GetField(Dart_RootLibrary(), Dart_NewStringFromCString("ticks")));
|
||||
});
|
||||
}
|
||||
|
||||
// Queue-based message handler, running on a separate thread.
|
||||
class ThreadedMessageHandler {
|
||||
public:
|
||||
void Run() {
|
||||
is_running = true;
|
||||
while (is_running) {
|
||||
Dart_Isolate isolate;
|
||||
{
|
||||
std::unique_lock notifications_lock(notifications_mutex_);
|
||||
can_pop_.wait(notifications_lock);
|
||||
if (notifications_.empty()) {
|
||||
continue;
|
||||
}
|
||||
isolate = notifications_.front();
|
||||
notifications_.pop();
|
||||
}
|
||||
DartEngine_HandleMessage(isolate);
|
||||
}
|
||||
}
|
||||
|
||||
void Notify(Dart_Isolate isolate) {
|
||||
std::unique_lock notifications_lock(notifications_mutex_);
|
||||
notifications_.push(isolate);
|
||||
can_pop_.notify_one();
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
is_running = false;
|
||||
can_pop_.notify_one();
|
||||
}
|
||||
|
||||
static void ScheduleDartMessage(Dart_Isolate isolate, void* context) {
|
||||
reinterpret_cast<ThreadedMessageHandler*>(context)->Notify(isolate);
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<Dart_Isolate> notifications_;
|
||||
std::condition_variable can_pop_;
|
||||
std::atomic<bool> is_running;
|
||||
std::mutex notifications_mutex_;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc == 1) {
|
||||
std::cerr << "Must specify snapshot path" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
char* error = nullptr;
|
||||
|
||||
// Start an event loop on a separate thread and use it as a default
|
||||
// scheduler.
|
||||
ThreadedMessageHandler message_handler;
|
||||
std::thread message_handler_thread(&ThreadedMessageHandler::Run,
|
||||
&message_handler);
|
||||
DartEngine_SetDefaultMessageScheduler(
|
||||
{ThreadedMessageHandler::ScheduleDartMessage, &message_handler});
|
||||
|
||||
// Load snapshot and create an isolate
|
||||
DartEngine_SnapshotData snapshot_data = AutoSnapshotFromFile(argv[1], &error);
|
||||
CheckError(error, "reading snapshot");
|
||||
Dart_Isolate isolate = DartEngine_CreateIsolate(snapshot_data, &error);
|
||||
CheckError(error, "creating isolate");
|
||||
|
||||
// Call Dart function to start a timer.
|
||||
StartTimer(isolate, 1);
|
||||
|
||||
// Wait a bit.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// Stop the timer.
|
||||
StopTimer(isolate);
|
||||
|
||||
// Get timer value.
|
||||
std::cout << "Ticks: " << GetTicks(isolate) << std::endl;
|
||||
|
||||
// Stop event loop.
|
||||
message_handler.Stop();
|
||||
message_handler_thread.join();
|
||||
|
||||
DartEngine_Shutdown();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// Same as run_timer.cc, but uses std::async instead of a dedicated event loop
|
||||
// thread. Used as a demonstration of a custom message scheduler.
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include "helpers.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_engine.h"
|
||||
|
||||
// Calls `startTimer` from timer.dart
|
||||
void StartTimer(Dart_Isolate isolate, uint32_t millis) {
|
||||
WithIsolate(isolate, [&]() {
|
||||
std::initializer_list<Dart_Handle> args{Dart_NewInteger(millis)};
|
||||
CheckError(
|
||||
Dart_Invoke(Dart_RootLibrary(), Dart_NewStringFromCString("startTimer"),
|
||||
1, const_cast<Dart_Handle*>(args.begin())),
|
||||
"calling startTimer");
|
||||
});
|
||||
}
|
||||
|
||||
// Calls `stopTimer` from timer.dart
|
||||
void StopTimer(Dart_Isolate isolate) {
|
||||
WithIsolate(isolate, [&]() {
|
||||
CheckError(Dart_Invoke(Dart_RootLibrary(),
|
||||
Dart_NewStringFromCString("stopTimer"), 0, nullptr),
|
||||
"calling stopTimer");
|
||||
});
|
||||
}
|
||||
|
||||
// Gets `ticks` from timer.dart
|
||||
int64_t GetTicks(Dart_Isolate isolate) {
|
||||
return WithIsolate<int64_t>(isolate, []() {
|
||||
return IntFromHandle(
|
||||
Dart_GetField(Dart_RootLibrary(), Dart_NewStringFromCString("ticks")));
|
||||
});
|
||||
}
|
||||
|
||||
std::mutex shutdown_mutex;
|
||||
|
||||
void ScheduleDartMessage(Dart_Isolate isolate, void* context) {
|
||||
std::ignore = std::async(DartEngine_HandleMessage, isolate);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc == 1) {
|
||||
std::cerr << "Must specify snapshot path" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
char* error = nullptr;
|
||||
|
||||
// Start an event loop on a separate thread and use it as a default
|
||||
// scheduler.
|
||||
DartEngine_MessageScheduler scheduler{ScheduleDartMessage, nullptr};
|
||||
DartEngine_SetDefaultMessageScheduler(scheduler);
|
||||
|
||||
// Load snapshot and create an isolate
|
||||
DartEngine_SnapshotData snapshot_data = AutoSnapshotFromFile(argv[1], &error);
|
||||
CheckError(error, "reading snapshot");
|
||||
Dart_Isolate isolate = DartEngine_CreateIsolate(snapshot_data, &error);
|
||||
CheckError(error, "creating isolate");
|
||||
|
||||
// Call Dart function to start a timer.
|
||||
StartTimer(isolate, 1);
|
||||
|
||||
// Wait a bit.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
// Stop the timer.
|
||||
StopTimer(isolate);
|
||||
|
||||
// Get timer value.
|
||||
std::cout << "Ticks: " << GetTicks(isolate) << std::endl;
|
||||
|
||||
DartEngine_Shutdown();
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include <iostream>
|
||||
#include "helpers.h"
|
||||
#include "include/dart_api.h"
|
||||
#include "include/dart_engine.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 3) {
|
||||
std::cerr << "Must specify two snapshot paths" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
char* error = nullptr;
|
||||
|
||||
DartEngine_SnapshotData snapshot1 = AutoSnapshotFromFile(argv[1], &error);
|
||||
CheckError(error, "reading snapshot");
|
||||
|
||||
DartEngine_SnapshotData snapshot2 = AutoSnapshotFromFile(argv[2], &error);
|
||||
CheckError(error, "reading snapshot");
|
||||
|
||||
Dart_Isolate isolate1 = DartEngine_CreateIsolate(snapshot1, &error);
|
||||
CheckError(error, "starting 1st isolate");
|
||||
|
||||
Dart_Isolate isolate2 = DartEngine_CreateIsolate(snapshot2, &error);
|
||||
CheckError(error, "starting 2nd isolate");
|
||||
|
||||
DartEngine_AcquireIsolate(isolate1);
|
||||
Dart_EnterScope();
|
||||
|
||||
Dart_Handle invoke_result = Dart_Invoke(
|
||||
Dart_RootLibrary(), Dart_NewStringFromCString("getValue"), 0, nullptr);
|
||||
std::string return_value = StringFromHandle(invoke_result);
|
||||
|
||||
std::cout << "program1 returned: " << return_value << std::endl;
|
||||
Dart_ExitScope();
|
||||
DartEngine_ReleaseIsolate();
|
||||
|
||||
DartEngine_AcquireIsolate(isolate2);
|
||||
Dart_EnterScope();
|
||||
|
||||
std::initializer_list<Dart_Handle> args{
|
||||
Dart_NewStringFromCString(return_value.c_str())};
|
||||
Dart_Handle invoke_result2 =
|
||||
Dart_Invoke(Dart_RootLibrary(), Dart_NewStringFromCString("printValue"),
|
||||
1, const_cast<Dart_Handle*>(args.begin()));
|
||||
CheckError(invoke_result2);
|
||||
|
||||
Dart_ExitScope();
|
||||
DartEngine_ReleaseIsolate();
|
||||
|
||||
DartEngine_Shutdown();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'dart:async';
|
||||
|
||||
void main() {
|
||||
throw 'Unimplemented';
|
||||
}
|
||||
|
||||
var _tickCount = 0;
|
||||
Timer? _timer;
|
||||
|
||||
@pragma('vm:entry-point', 'call')
|
||||
void startTimer(int millis) {
|
||||
if (_timer == null) {
|
||||
final period = Duration(milliseconds: millis);
|
||||
_timer = Timer.periodic(period, (_) {
|
||||
_tickCount++;
|
||||
});
|
||||
print('Started timer with period $period');
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point', 'call')
|
||||
void stopTimer() {
|
||||
_timer?.cancel();
|
||||
_timer = null;
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point', 'call')
|
||||
void resetTimer() {
|
||||
_tickCount = 0;
|
||||
}
|
||||
|
||||
@pragma('vm:entry-point', 'get')
|
||||
int get ticks => _tickCount;
|
||||
@@ -2,17 +2,26 @@ import 'dart:io';
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
void main() {
|
||||
final executable = Platform.executable;
|
||||
final outDir = executable.substring(0, executable.lastIndexOf('dart'));
|
||||
|
||||
final runKernelExecutable = outDir + 'run_kernel';
|
||||
|
||||
final result = Process.runSync(runKernelExecutable, []);
|
||||
void checkSample(
|
||||
String binary,
|
||||
List<String> args, {
|
||||
bool skipIfNotBuilt = false,
|
||||
}) {
|
||||
if (Platform.isWindows) {
|
||||
binary = '$binary.exe';
|
||||
}
|
||||
if (!File(binary).existsSync()) {
|
||||
if (skipIfNotBuilt) {
|
||||
return;
|
||||
} else {
|
||||
Expect.fail('Binary $binary does not exist!');
|
||||
}
|
||||
}
|
||||
final result = Process.runSync(binary, args);
|
||||
Expect.equals(
|
||||
0,
|
||||
result.exitCode,
|
||||
'process failed:\n'
|
||||
'process $binary failed:\n'
|
||||
' exit code: ${result.exitCode}\n'
|
||||
' -- stdout --\n'
|
||||
'${result.stdout}'
|
||||
@@ -21,3 +30,33 @@ void main() {
|
||||
' ------------',
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
final executable = Platform.executable;
|
||||
final out = executable.substring(0, executable.lastIndexOf('dart') - 1);
|
||||
|
||||
checkSample('$out/run_main_kernel', ['$out/gen/hello_kernel.dart.snapshot']);
|
||||
checkSample('$out/run_two_programs_kernel', [
|
||||
'$out/gen/program1_kernel.dart.snapshot',
|
||||
'$out/gen/program2_kernel.dart.snapshot',
|
||||
]);
|
||||
checkSample('$out/run_timer_kernel', ['$out/gen/timer_kernel.dart.snapshot']);
|
||||
checkSample('$out/run_timer_async_kernel', [
|
||||
'$out/gen/timer_kernel.dart.snapshot',
|
||||
]);
|
||||
|
||||
// AOT Samples aren't built on some platforms.
|
||||
checkSample('$out/run_main_aot', [
|
||||
'$out/hello_aot.snapshot',
|
||||
], skipIfNotBuilt: true);
|
||||
checkSample('$out/run_two_programs_aot', [
|
||||
'$out/program1_aot.snapshot',
|
||||
'$out/program2_aot.snapshot',
|
||||
], skipIfNotBuilt: true);
|
||||
checkSample('$out/run_timer_aot', [
|
||||
'$out/timer_aot.snapshot',
|
||||
], skipIfNotBuilt: true);
|
||||
checkSample('$out/run_timer_async_aot', [
|
||||
'$out/timer_aot.snapshot',
|
||||
], skipIfNotBuilt: true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user