Skip to content

Commit

Permalink
Standalone Dart runtime library for embedders
Browse files Browse the repository at this point in the history
For now we use a static library, but I am planning to add support for a shared library in a follow-up CL.

Tested: locally on macOS and Linux, also ci should at least build samples.
Change-Id: I277239359de226c56633884f350b22a030759ab6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/392640
Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>
Reviewed-by: Slava Egorov <vegorov@google.com>
  • Loading branch information
iinozemtsev authored and Commit Queue committed Nov 6, 2024
1 parent 76717f2 commit 53ba9af
Show file tree
Hide file tree
Showing 6 changed files with 250 additions and 0 deletions.
5 changes: 5 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,14 @@ group("most") {
":dartanalyzer",
":ddc",
":runtime",
":samples",
]
}

group("samples") {
deps = [ "samples/embedder:all" ]
}

group("runtime") {
import("runtime/runtime_args.gni")

Expand Down
29 changes: 29 additions & 0 deletions runtime/bin/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1193,3 +1193,32 @@ if (defined(is_linux) && is_linux && defined(is_asan) && is_asan &&
}
}
}

source_set("dart_embedder_runtime_jit_set") {
include_dirs = [
"..",
"//third_party/boringssl/src/include",
"//third_party",
]

sources = [
"dart_embedder_api_impl.cc",
"dfe.cc",
"dfe.h",
"vmservice_impl.cc",
"vmservice_impl.h",
]

deps = [
":dart_io_api",
":dart_kernel_platform_cc",
":libdart_builtin",
"..:libdart_jit",
]
}

static_library("dart_embedder_runtime_jit") {
complete_static_lib = true
output_name = "dart_embedder_runtime_jit"
deps = [ ":dart_embedder_runtime_jit_set" ]
}
25 changes: 25 additions & 0 deletions samples/embedder/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 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.

import("../../utils/application_snapshot.gni")

# All samples.
group("all") {
deps = [ ":run_kernel" ]
}

# 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" ]
}

# Kernel snapshot of ./hello.dart.
application_snapshot("hello_kernel") {
main_dart = "hello.dart"
dart_snapshot_kind = "kernel"
training_args = [] # Not used
}
27 changes: 27 additions & 0 deletions samples/embedder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dart VM Embedding examples

Examples of using Dart VM and executing Dart code from C++ binaries.

## run_kernel.cc

To run the example:

```sh
./tools/build.py --no-rbe --mode=release samples/embedder:run_kernel && out/ReleaseX64/run_kernel
```

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:

```sh
dart compile kernel --no-link-platform my.dart
out/ReleaseX64/run_kernel my.dill
```

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).
10 changes: 10 additions & 0 deletions samples/embedder/hello.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// 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.

import 'package:collection/collection.dart';

void main(List<String>? args) {
final greetee = args?.singleOrNull ?? 'world';
print('Hello, $greetee!');
}
154 changes: 154 additions & 0 deletions samples/embedder/run_kernel.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// 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(&params, 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);
return std::string(bytes, length);
}

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.
CheckError(dart::embedder::InitOnce(&error), 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_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 comments on commit 53ba9af

Please sign in to comment.