diff --git a/BUILD.gn b/BUILD.gn index d64929636f60..72a56bda28fe 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -29,9 +29,14 @@ group("most") { ":dartanalyzer", ":ddc", ":runtime", + ":samples", ] } +group("samples") { + deps = [ "samples/embedder:all" ] +} + group("runtime") { import("runtime/runtime_args.gni") diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn index c06e61945291..c4a2388ed8a0 100644 --- a/runtime/bin/BUILD.gn +++ b/runtime/bin/BUILD.gn @@ -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" ] +} diff --git a/samples/embedder/BUILD.gn b/samples/embedder/BUILD.gn new file mode 100644 index 000000000000..b3a87b3ea394 --- /dev/null +++ b/samples/embedder/BUILD.gn @@ -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 +} diff --git a/samples/embedder/README.md b/samples/embedder/README.md new file mode 100644 index 000000000000..c605eea2afa3 --- /dev/null +++ b/samples/embedder/README.md @@ -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). diff --git a/samples/embedder/hello.dart b/samples/embedder/hello.dart new file mode 100644 index 000000000000..8101c81b9020 --- /dev/null +++ b/samples/embedder/hello.dart @@ -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? args) { + final greetee = args?.singleOrNull ?? 'world'; + print('Hello, $greetee!'); +} diff --git a/samples/embedder/run_kernel.cc b/samples/embedder/run_kernel.cc new file mode 100644 index 000000000000..3686ffc137d1 --- /dev/null +++ b/samples/embedder/run_kernel.cc @@ -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 +#include +#include +#include +#include +#include +#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(std::malloc(length)); + source_file.read(bytes, length); + return std::string(bytes, length); +} + +Dart_Handle ToDartStringList(const std::vector& 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 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(snapshot_data.c_str()), + snapshot_data.size()), + "Dart_LoadLibraryFromKernel"); + + // Call main function with args. + std::initializer_list main_args{ToDartStringList({"universe"})}; + CheckHandle(Dart_Invoke(library, Dart_NewStringFromCString("main"), 1, + const_cast(main_args.begin())), + "Dart_Invoke('main')"); + Dart_ExitScope(); + Dart_ShutdownIsolate(); +}