├── examples ├── realtime_example │ ├── .gitignore │ ├── drawable.cpp │ ├── dart │ │ ├── pubspec.yaml │ │ ├── .vscode │ │ │ └── launch.json │ │ ├── drawable.dart │ │ ├── ffi_calls.dart │ │ └── main.dart │ ├── drawable.h │ ├── CMakeLists.txt │ └── main.cpp ├── simple_example_ffi │ ├── .gitignore │ ├── pubspec.yaml │ ├── hello_world_ffi.dart │ ├── CMakeLists.txt │ └── main.cpp ├── simple_example │ ├── hello_world.dart │ ├── CMakeLists.txt │ └── main.cpp ├── CMakeLists.txt └── isolate_spawn │ ├── hello_world.dart │ ├── CMakeLists.txt │ └── main.cpp ├── .gitattributes ├── .dart_version ├── scripts └── build_helpers │ ├── .gitignore │ ├── pubspec.yaml │ ├── analysis_options.yaml │ ├── bin │ ├── assemble_artifacts.dart │ └── build_dart.dart │ └── lib │ ├── build_helpers.dart │ └── depot_tools.dart ├── .gitignore ├── setup_env.ps1 ├── .vscode ├── launch.json └── settings.json ├── CMakeLists.txt ├── .clang-format ├── LICENSE ├── src ├── isolate_setup.h ├── dart_dll.h ├── CMakeLists.txt ├── dart_dll.cpp └── isolate_setup.cpp ├── .github └── workflows │ └── build.yaml ├── dart_sdk.patch └── README.md /examples/realtime_example/.gitignore: -------------------------------------------------------------------------------- 1 | .dart_tool/ 2 | pubspec.lock -------------------------------------------------------------------------------- /examples/simple_example_ffi/.gitignore: -------------------------------------------------------------------------------- 1 | .dart_tool/ 2 | pubspec.lock -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Default 2 | * text=auto 3 | 4 | dart_sdk.patch text eol=lf 5 | -------------------------------------------------------------------------------- /.dart_version: -------------------------------------------------------------------------------- 1 | # This file contains the current Dart version we build against 2 | 3.8.2 -------------------------------------------------------------------------------- /examples/realtime_example/drawable.cpp: -------------------------------------------------------------------------------- 1 | #include "drawable.h" 2 | 3 | #include "cute.h" 4 | using namespace Cute; -------------------------------------------------------------------------------- /scripts/build_helpers/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | pubspec.lock -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dart-sdk/* 2 | .build/* 3 | build/* 4 | artifacts/* 5 | depot_tools/* 6 | out/* 7 | .vs 8 | **/.DS_Store 9 | depot_tools/ 10 | -------------------------------------------------------------------------------- /examples/realtime_example/dart/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: realtime_example 2 | environment: 3 | sdk: ">=3.0.0 <4.0.0" 4 | 5 | dependencies: 6 | ffi: ^2.1.0 -------------------------------------------------------------------------------- /examples/simple_example_ffi/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: simple_example_ffi 2 | environment: 3 | sdk: ">=3.0.0 <4.0.0" 4 | 5 | dependencies: 6 | ffi: ^2.1.0 -------------------------------------------------------------------------------- /setup_env.ps1: -------------------------------------------------------------------------------- 1 | $Env:GYP_MSVS_OVERRIDE_PATH="C:\Program Files\Microsoft Visual Studio\2022\Community\" 2 | $Env:GYP_MSVS_VERSION=2019 3 | $Env:DEPOT_TOOLS_WIN_TOOLCHAIN=0 4 | -------------------------------------------------------------------------------- /examples/simple_example/hello_world.dart: -------------------------------------------------------------------------------- 1 | @pragma('vm:external-name','SimplePrint') 2 | external void simplePrint(String s); 3 | 4 | void main() { 5 | simplePrint("Hello From Dart!\n"); 6 | } 7 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | add_subdirectory(isolate_spawn) 4 | add_subdirectory(simple_example) 5 | add_subdirectory(simple_example_ffi) 6 | add_subdirectory(realtime_example) -------------------------------------------------------------------------------- /examples/realtime_example/drawable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | using namespace Cute; 6 | 7 | class Drawable { 8 | public: 9 | int x; 10 | int y; 11 | int width; 12 | int height; 13 | 14 | Color color; 15 | }; -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | ] 9 | } -------------------------------------------------------------------------------- /scripts/build_helpers/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: build_helpers 2 | description: Script to help build the Dart Shared Library 3 | version: 1.0.0 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ^3.3.0 8 | 9 | dependencies: 10 | args: ^2.4.2 11 | path: ^1.8.0 12 | logger: ^2.0.2 13 | glob: ^2.1.2 14 | 15 | dev_dependencies: 16 | lints: ^3.0.0 17 | test: ^1.21.0 18 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, // this is a default value 4 | "**/.DS_Store": true, // this is a default value 5 | 6 | "dart-sdk/": true 7 | }, 8 | "dart.analysisExcludedFolders": [ 9 | "dart-sdk" 10 | ], 11 | "files.associations": { 12 | "xiosbase": "cpp" 13 | } 14 | } -------------------------------------------------------------------------------- /examples/simple_example_ffi/hello_world_ffi.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | import 'package:ffi/ffi.dart'; 3 | 4 | @Native)>(symbol: "SimplePrint", isLeaf: true) 5 | external void simplePrint(Pointer string); 6 | 7 | void main() { 8 | var string = "Hello From Dart!\n"; 9 | var c_string = string.toNativeUtf8(); 10 | 11 | simplePrint(c_string); 12 | 13 | malloc.free(c_string); 14 | } 15 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(DartSharedLibrary VERSION 0.1) 4 | 5 | option(BUILD_SAMPLES "Build the Samples" ON) 6 | 7 | set(CMAKE_CXX_STANDARD 11) 8 | set(CMAKE_CXX_STANDARD_REQUIRED True) 9 | set(DART_DLL_DIR "${PROJECT_SOURCE_DIR}/src") 10 | set(DART_DIR "${PROJECT_SOURCE_DIR}/dart-sdk/sdk") 11 | 12 | add_subdirectory(src) 13 | if(BUILD_SAMPLES) 14 | add_subdirectory(examples) 15 | endif() -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Defines the Chromium style for automatic reformatting. 2 | # http://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | BasedOnStyle: Chromium 4 | 5 | # clang-format doesn't seem to do a good job of this for longer comments. 6 | ReflowComments: 'false' 7 | 8 | # We have lots of these. Though we need to put them all in curly braces, 9 | # clang-format can't do that. 10 | AllowShortIfStatementsOnASingleLine: 'true' 11 | 12 | # Put escaped newlines into the rightmost column. 13 | AlignEscapedNewlinesLeft: false 14 | -------------------------------------------------------------------------------- /examples/realtime_example/dart/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Dart: Attach to Process", 9 | "type": "dart", 10 | "request": "attach", 11 | "vmServiceUri": "http://127.0.0.1:5858/" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /examples/isolate_spawn/hello_world.dart: -------------------------------------------------------------------------------- 1 | import 'dart:isolate'; 2 | 3 | @pragma('vm:external-name', 'SimplePrint') 4 | external void simplePrint(String s); 5 | 6 | Future _isolateWorker() async { 7 | simplePrint("Isolate work 1..."); 8 | await Future.delayed(Duration(seconds: 10)); 9 | simplePrint("Isolate work 2..."); 10 | return "done"; 11 | } 12 | 13 | void main() async { 14 | simplePrint("Hello From Dart!\n"); 15 | final isolateValue = await Isolate.run(_isolateWorker); 16 | simplePrint("Isolate complete, returned $isolateValue"); 17 | } 18 | -------------------------------------------------------------------------------- /examples/realtime_example/dart/drawable.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | final class CF_Color extends Struct { 4 | @Float() 5 | external double r; 6 | 7 | @Float() 8 | external double g; 9 | 10 | @Float() 11 | external double b; 12 | 13 | @Float() 14 | external double a; 15 | } 16 | 17 | final class Drawable extends Struct { 18 | @Int32() 19 | external int x; 20 | 21 | @Int32() 22 | external int y; 23 | 24 | @Int32() 25 | external int width; 26 | 27 | @Int32() 28 | external int height; 29 | 30 | external CF_Color color; 31 | } 32 | -------------------------------------------------------------------------------- /examples/isolate_spawn/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(isolate_spawn) 4 | 5 | add_executable(isolate_spawn main.cpp) 6 | 7 | target_include_directories(isolate_spawn PRIVATE 8 | "${DART_DLL_DIR}" 9 | "${DART_DIR}/runtime/include" 10 | ) 11 | 12 | if(WIN32) 13 | add_custom_command(TARGET isolate_spawn POST_BUILD 14 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 15 | COMMAND_EXPAND_LISTS 16 | ) 17 | endif() 18 | 19 | add_custom_command(TARGET isolate_spawn POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/hello_world.dart $ 21 | COMMAND_EXPAND_LISTS 22 | ) 23 | 24 | target_link_libraries(isolate_spawn PUBLIC dart_dll) 25 | 26 | if (MSVC) 27 | set_property(TARGET isolate_spawn PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $) 28 | endif() -------------------------------------------------------------------------------- /examples/simple_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(simple_example) 4 | 5 | add_executable(simple_example main.cpp) 6 | 7 | target_include_directories(simple_example PRIVATE 8 | "${DART_DLL_DIR}" 9 | "${DART_DIR}/runtime/include" 10 | ) 11 | 12 | if(WIN32) 13 | add_custom_command(TARGET simple_example POST_BUILD 14 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 15 | COMMAND_EXPAND_LISTS 16 | ) 17 | endif() 18 | 19 | add_custom_command(TARGET simple_example POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/hello_world.dart $ 21 | COMMAND_EXPAND_LISTS 22 | ) 23 | 24 | target_link_libraries(simple_example PUBLIC dart_dll) 25 | 26 | if (MSVC) 27 | set_property(TARGET simple_example PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $) 28 | endif() -------------------------------------------------------------------------------- /scripts/build_helpers/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | linter: 19 | rules: 20 | - camel_case_types 21 | - prefer_relative_imports 22 | 23 | # analyzer: 24 | # exclude: 25 | # - path/to/excluded/files/** -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Released under MIT License 2 | 3 | Copyright (c) 2022 Jeff Ward 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /examples/simple_example_ffi/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(simple_example_ffi) 4 | 5 | add_executable(simple_example_ffi main.cpp) 6 | 7 | target_include_directories(simple_example_ffi PRIVATE 8 | "${DART_DLL_DIR}" 9 | "${DART_DIR}/runtime/include" 10 | ) 11 | 12 | if(WIN32) 13 | add_custom_command(TARGET simple_example_ffi POST_BUILD 14 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 15 | COMMAND_EXPAND_LISTS 16 | ) 17 | endif() 18 | 19 | add_custom_command(TARGET simple_example_ffi POST_BUILD 20 | COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/hello_world_ffi.dart $ 21 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/.dart_tool $/.dart_tool 22 | COMMAND_EXPAND_LISTS 23 | ) 24 | 25 | target_link_libraries(simple_example_ffi PUBLIC dart_dll) 26 | 27 | if (MSVC) 28 | set_property(TARGET simple_example_ffi PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $) 29 | endif() 30 | -------------------------------------------------------------------------------- /examples/realtime_example/dart/ffi_calls.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import 'drawable.dart'; 4 | 5 | class WorkFfiCalls { 6 | final DynamicLibrary processLib = DynamicLibrary.process(); 7 | 8 | late final createEntity = processLib 9 | .lookup>( 10 | 'create_entity') 11 | .asFunction(isLeaf: true); 12 | late final destroyEntity = processLib 13 | .lookup>('destroy_entity') 14 | .asFunction(isLeaf: true); 15 | late final getDrawable = processLib 16 | .lookup Function(Uint32)>>( 17 | 'get_drawable') 18 | .asFunction Function(int)>(isLeaf: true); 19 | 20 | late final getKeyJustPressed = processLib 21 | .lookup>('get_key_just_pressed') 22 | .asFunction(isLeaf: true); 23 | } 24 | 25 | const int CF_KEY_RIGHT = 145; 26 | const int CF_KEY_LEFT = 146; 27 | const int CF_KEY_DOWN = 147; 28 | const int CF_KEY_UP = 148; 29 | -------------------------------------------------------------------------------- /examples/realtime_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(realtime_example) 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | set(CUTE_FRAMEWORK_STATIC ON) 7 | set(CF_FRAMEWORK_BUILD_TESTS OFF) 8 | # Samples Build on Windows Falied 9 | set(CF_FRAMEWORK_BUILD_SAMPLES OFF) 10 | 11 | include(FetchContent) 12 | FetchContent_Declare( 13 | cute 14 | GIT_REPOSITORY https://github.com/RandyGaul/cute_framework 15 | GIT_TAG a2341c72f02e019e157cfc3997cd81d98c825004 16 | ) 17 | FetchContent_MakeAvailable(cute) 18 | 19 | add_executable(realtime_example 20 | main.cpp 21 | drawable.cpp 22 | ) 23 | 24 | target_include_directories(realtime_example PRIVATE 25 | "." 26 | "${DART_DLL_DIR}" 27 | "${DART_DIR}/runtime/include" 28 | ) 29 | 30 | if(LINUX) 31 | # -export-dynamic is required on Linux if your symbols are in your executable, 32 | # otherwise dlsym (and therefore Dart FFI) can't find them. 33 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-export-dynamic") 34 | endif() 35 | 36 | if (WIN32) 37 | add_custom_command(TARGET realtime_example POST_BUILD 38 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 39 | COMMAND_EXPAND_LISTS 40 | ) 41 | endif() 42 | 43 | add_custom_command(TARGET realtime_example POST_BUILD 44 | COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/dart/ $/dart 45 | COMMAND_EXPAND_LISTS 46 | ) 47 | 48 | target_link_libraries(realtime_example PUBLIC dart_dll cute) 49 | 50 | if (MSVC) 51 | set_property(TARGET realtime_example PROPERTY VS_DEBUGGER_WORKING_DIRECTORY $) 52 | endif() 53 | -------------------------------------------------------------------------------- /examples/realtime_example/dart/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ffi'; 2 | 3 | import 'ffi_calls.dart'; 4 | 5 | WorkFfiCalls ffi = new WorkFfiCalls(); 6 | 7 | class Wall { 8 | late int entity; 9 | 10 | Wall(int x, int y, int width, int height) { 11 | entity = ffi.createEntity(x, y, width, height); 12 | final drawable = ffi.getDrawable(entity); 13 | drawable.ref.color.r = 1.0; 14 | drawable.ref.color.g = 0.0; 15 | drawable.ref.color.b = 0.0; 16 | drawable.ref.color.a = 1.0; 17 | } 18 | } 19 | 20 | List walls = []; 21 | 22 | // Dot! 23 | final int speed = 80; 24 | int dotEntity = 0; 25 | double dotX = 0; 26 | double dotY = 0; 27 | bool movingLeft = true; 28 | 29 | @pragma("vm:entry-point") 30 | void main() { 31 | print('main'); 32 | walls.add( 33 | Wall(-320, -240, 5, 480), 34 | ); 35 | walls.add( 36 | Wall(315, -240, 5, 480), 37 | ); 38 | walls.add( 39 | Wall(-320, -240, 640, 5), 40 | ); 41 | walls.add( 42 | Wall(-320, 235, 640, 5), 43 | ); 44 | 45 | dotEntity = ffi.createEntity(0, 0, 10, 10); 46 | final drawable = ffi.getDrawable(dotEntity); 47 | drawable.ref.color.r = 0.0; 48 | drawable.ref.color.g = 1.0; 49 | drawable.ref.color.b = 0.0; 50 | } 51 | 52 | @pragma("vm:entry-point") 53 | void frame(double dt) { 54 | if (movingLeft) { 55 | dotX -= (speed * dt); 56 | if (dotX < -200) { 57 | dotX = -200; 58 | movingLeft = false; 59 | } 60 | } else { 61 | dotX += (speed * dt); 62 | if (dotX > 200) { 63 | dotX = 200; 64 | movingLeft = true; 65 | } 66 | } 67 | 68 | final dotDrawable = ffi.getDrawable(dotEntity); 69 | dotDrawable.ref.x = dotX.floor(); 70 | dotDrawable.ref.y = dotY.floor(); 71 | } 72 | -------------------------------------------------------------------------------- /examples/simple_example_ffi/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "dart_dll.h" 5 | 6 | #include 7 | 8 | Dart_Handle HandleError(Dart_Handle handle) { 9 | if (Dart_IsError(handle)) { 10 | Dart_PropagateError(handle); 11 | } 12 | return handle; 13 | } 14 | 15 | void SimplePrint(const char* string) { 16 | std::cout << "Hello from C++. Dart says:\n"; 17 | std::cout << string; 18 | } 19 | 20 | void* ResolveNativeFunction(const char* name, uintptr_t args_n) { 21 | void* native_function = nullptr; 22 | if (strcmp("SimplePrint", name) == 0) { 23 | native_function = reinterpret_cast(SimplePrint); 24 | } 25 | 26 | return native_function; 27 | } 28 | 29 | int main() { 30 | // Initialize Dart 31 | if (!DartDll_Initialize(DartDllConfig())) { 32 | return -1; 33 | } 34 | 35 | // Load your main isolate file, also providing the path to a package config if one exists. 36 | // The build makes sure these are coppied to the output directory for running the example 37 | Dart_Isolate isolate = DartDll_LoadScript("hello_world_ffi.dart", ".dart_tool/package_config.json"); 38 | if (isolate == nullptr) { 39 | return -1; 40 | } 41 | 42 | // With the library loaded, you can now use the dart_api.h functions 43 | // This includes setting the native function resolver: 44 | Dart_EnterIsolate(isolate); 45 | Dart_EnterScope(); 46 | 47 | Dart_Handle library = Dart_RootLibrary(); 48 | Dart_SetFfiNativeResolver(library, ResolveNativeFunction); 49 | 50 | // And run "main" 51 | Dart_Handle result = DartDll_RunMain(library); 52 | if (Dart_IsError(result)) { 53 | std::cerr << "Failed to invoke main: " << Dart_GetError(result); 54 | } 55 | 56 | Dart_ExitScope(); 57 | Dart_ShutdownIsolate(); 58 | 59 | // Don't forget to shutdown 60 | DartDll_Shutdown(); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /src/isolate_setup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using namespace dart::bin; 7 | 8 | Dart_Isolate CreateKernelIsolate(const char* script_uri, 9 | const char* main, 10 | const char* package_root, 11 | const char* package_config, 12 | Dart_IsolateFlags* flags, 13 | void* isolate_data, 14 | char** error); 15 | 16 | Dart_Isolate CreateVmServiceIsolate(const char* script_uri, 17 | const char* main, 18 | const char* package_root, 19 | const char* package_config, 20 | Dart_IsolateFlags* flags, 21 | void* isolate_data, 22 | int service_port, 23 | char** error); 24 | 25 | Dart_Isolate CreateIsolate(bool is_main_isolate, 26 | const char* script_uri, 27 | const char* name, 28 | const char* packages_config, 29 | Dart_IsolateFlags* flags, 30 | void* isolate_data, 31 | char** error); 32 | 33 | Dart_Handle SetupCoreLibraries(Dart_Isolate isolate, 34 | IsolateData* isolate_data, 35 | bool is_isolate_group_start, 36 | const char** resolved_packages_config); 37 | 38 | void* GetUserIsolateData(void* isolate_group_data); 39 | 40 | void DeleteIsolateData(void* raw_isolate_group_data, void* raw_isolate_data); 41 | void DeleteIsolateGroupData(void* raw_isolate_group_data); 42 | -------------------------------------------------------------------------------- /src/dart_dll.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct _Dart_Isolate* Dart_Isolate; 4 | typedef struct _Dart_Handle* Dart_Handle; 5 | 6 | #ifdef _WIN 7 | #ifdef DART_DLL_EXPORTING 8 | #define DART_DLL_EXPORT __declspec(dllexport) 9 | #else 10 | #define DART_DLL_EXPORT __declspec(dllimport) 11 | #endif 12 | #else 13 | #define DART_DLL_EXPORT 14 | #endif 15 | 16 | extern "C" { 17 | 18 | struct DartDllConfig { 19 | bool start_service_isolate; 20 | int service_port; 21 | 22 | DartDllConfig() 23 | : start_service_isolate(true) 24 | , service_port(5858) 25 | { 26 | 27 | } 28 | }; 29 | 30 | // Initialize Dart 31 | DART_DLL_EXPORT bool DartDll_Initialize(const DartDllConfig& config); 32 | 33 | // Load a script, with an optional package configuration location. The package 34 | // configuration is usually in ".dart_tool/package_config.json". 35 | DART_DLL_EXPORT Dart_Isolate DartDll_LoadScript(const char* script_uri, 36 | const char* package_config, 37 | void* isolate_data = nullptr); 38 | 39 | // Gets the pointer provided by the `isolate_data` parameter in DartDll_LoadScript after 40 | // calling Dart_CurrentIsolateData 41 | DART_DLL_EXPORT void* DartDll_GetUserIsolateData(void* isolate_group_data); 42 | 43 | // Run "main" from the supplied library, usually one you got from 44 | // Dart_RootLibrary() 45 | DART_DLL_EXPORT Dart_Handle DartDll_RunMain(Dart_Handle library); 46 | 47 | // Drain the microtask queue. This is necessary if you're using any async code 48 | // or Futures, and using Dart_Invoke over DartDll_RunMain or Dart_RunLoop. 49 | // Otherwise you're not giving the main isolate the opportunity to drain the task queue 50 | // and complete pending Futures. 51 | DART_DLL_EXPORT Dart_Handle DartDll_DrainMicrotaskQueue(); 52 | 53 | // Shutdown Dart 54 | DART_DLL_EXPORT bool DartDll_Shutdown(); 55 | } 56 | -------------------------------------------------------------------------------- /examples/simple_example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "dart_dll.h" 5 | 6 | #include 7 | 8 | Dart_Handle HandleError(Dart_Handle handle) { 9 | if (Dart_IsError(handle)) { 10 | Dart_PropagateError(handle); 11 | } 12 | return handle; 13 | } 14 | 15 | void SimplePrint(Dart_NativeArguments arguments) { 16 | Dart_Handle string = HandleError(Dart_GetNativeArgument(arguments, 0)); 17 | if (Dart_IsString(string)) { 18 | const char* cstring; 19 | Dart_StringToCString(string, &cstring); 20 | std::cout << "Hello from C++. Dart says:\n"; 21 | std::cout << cstring; 22 | } 23 | } 24 | 25 | Dart_NativeFunction ResolveNativeFunction(Dart_Handle name, 26 | int /* argc */, 27 | bool* /* auto_setup_scope */) { 28 | if (!Dart_IsString(name)) { 29 | return nullptr; 30 | } 31 | 32 | Dart_NativeFunction result = nullptr; 33 | 34 | const char* cname; 35 | HandleError(Dart_StringToCString(name, &cname)); 36 | 37 | if (strcmp("SimplePrint", cname) == 0) { 38 | result = SimplePrint; 39 | } 40 | 41 | return result; 42 | } 43 | 44 | int main() { 45 | // Initialize Dart 46 | DartDllConfig config; 47 | config.service_port = 6222; 48 | 49 | if (!DartDll_Initialize(config)) { 50 | return -1; 51 | } 52 | 53 | // Load your main isolate file, also providing the path to a package config if one exists. 54 | // The build makes sure these are coppied to the output directory for running the example 55 | Dart_Isolate isolate = DartDll_LoadScript("hello_world.dart", nullptr); 56 | if (isolate == nullptr) { 57 | return -1; 58 | } 59 | 60 | // With the library loaded, you can now use the dart_api.h functions 61 | // This includes setting the native function resolver: 62 | Dart_EnterIsolate(isolate); 63 | Dart_EnterScope(); 64 | 65 | Dart_Handle library = Dart_RootLibrary(); 66 | Dart_SetNativeResolver(library, ResolveNativeFunction, nullptr); 67 | 68 | // And run "main" 69 | Dart_Handle result = DartDll_RunMain(library); 70 | if (Dart_IsError(result)) { 71 | std::cerr << "Failed to invoke main: " << Dart_GetError(result); 72 | } 73 | 74 | Dart_ExitScope(); 75 | Dart_ShutdownIsolate(); 76 | 77 | // Don't forget to shutdown 78 | DartDll_Shutdown(); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /examples/isolate_spawn/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "dart_dll.h" 5 | 6 | #include 7 | 8 | Dart_Handle HandleError(Dart_Handle handle) { 9 | if (Dart_IsError(handle)) { 10 | Dart_PropagateError(handle); 11 | } 12 | return handle; 13 | } 14 | 15 | void SimplePrint(Dart_NativeArguments arguments) { 16 | Dart_Handle string = HandleError(Dart_GetNativeArgument(arguments, 0)); 17 | if (Dart_IsString(string)) { 18 | const char* cstring; 19 | Dart_StringToCString(string, &cstring); 20 | std::cout << "Hello from C++. Dart says:\n"; 21 | std::cout << cstring << std::endl; 22 | } 23 | } 24 | 25 | Dart_NativeFunction ResolveNativeFunction(Dart_Handle name, 26 | int /* argc */, 27 | bool* /* auto_setup_scope */) { 28 | if (!Dart_IsString(name)) { 29 | return nullptr; 30 | } 31 | 32 | Dart_NativeFunction result = nullptr; 33 | 34 | const char* cname; 35 | HandleError(Dart_StringToCString(name, &cname)); 36 | 37 | if (strcmp("SimplePrint", cname) == 0) { 38 | result = SimplePrint; 39 | } 40 | 41 | return result; 42 | } 43 | 44 | int main() { 45 | // Initialize Dart 46 | DartDllConfig config; 47 | config.service_port = 6222; 48 | 49 | if (!DartDll_Initialize(config)) { 50 | return -1; 51 | } 52 | 53 | // Load your main isolate file, also providing the path to a package config if one exists. 54 | // The build makes sure these are coppied to the output directory for running the example 55 | Dart_Isolate isolate = DartDll_LoadScript("hello_world.dart", nullptr); 56 | if (isolate == nullptr) { 57 | return -1; 58 | } 59 | 60 | // With the library loaded, you can now use the dart_api.h functions 61 | // This includes setting the native function resolver: 62 | Dart_EnterIsolate(isolate); 63 | Dart_EnterScope(); 64 | 65 | Dart_Handle library = Dart_RootLibrary(); 66 | Dart_SetNativeResolver(library, ResolveNativeFunction, nullptr); 67 | 68 | // And run "main" 69 | Dart_Handle result = DartDll_RunMain(library); 70 | if (Dart_IsError(result)) { 71 | std::cerr << "Failed to invoke main: " << Dart_GetError(result); 72 | } 73 | 74 | Dart_ExitScope(); 75 | Dart_ShutdownIsolate(); 76 | 77 | // Don't forget to shutdown 78 | DartDll_Shutdown(); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /scripts/build_helpers/bin/assemble_artifacts.dart: -------------------------------------------------------------------------------- 1 | // Script to assemble artifacts into a single place 2 | import 'dart:io'; 3 | 4 | import 'package:build_helpers/build_helpers.dart'; 5 | import 'package:glob/glob.dart'; 6 | import 'package:glob/list_local_fs.dart'; 7 | import 'package:logger/logger.dart'; 8 | import 'package:path/path.dart' as path; 9 | 10 | void main() { 11 | if (!checkRightDirectory()) { 12 | // Not run from root. Exit. 13 | exit(-1); 14 | } 15 | // Always verbose 16 | BuildToolsLogger.initLogger(logLevel: Level.debug); 17 | 18 | final dest = Directory('artifacts'); 19 | dest.createSync(); 20 | _copyIncludeFiles(dest); 21 | _copyLibs(dest); 22 | } 23 | 24 | void _copyIncludeFiles(Directory dest) { 25 | final logger = BuildToolsLogger.shared; 26 | 27 | final includePath = Directory('dart-sdk/sdk/runtime/include'); 28 | if (!includePath.existsSync()) { 29 | logger.f("Couldn't find Dart SDK include dir."); 30 | exit(-1); 31 | } 32 | 33 | const dartIncludeFiles = ['dart_api.h', 'dart_tools_api.h']; 34 | Directory(path.join(dest.path, 'include')).createSync(recursive: true); 35 | for (var dartIncludeFile in dartIncludeFiles) { 36 | final file = File(path.join(includePath.path, dartIncludeFile)); 37 | final destPath = path.join(dest.path, 'include', dartIncludeFile); 38 | logger.i(' ${file.path} => $destPath'); 39 | file.copySync(destPath); 40 | } 41 | 42 | final dartDllHeader = File('src/dart_dll.h'); 43 | dartDllHeader.copySync(path.join(dest.path, 'include', 'dart_dll.h')); 44 | } 45 | 46 | void _copyLibs(Directory dest) { 47 | final logger = BuildToolsLogger.shared; 48 | 49 | final builtLibPath = Directory(path.join('build', 'src')); 50 | if (!builtLibPath.existsSync()) { 51 | logger.f('Could not find built artifact path'); 52 | } 53 | 54 | final binDestPath = Directory(path.join(dest.path, 'bin')); 55 | binDestPath.createSync(recursive: true); 56 | 57 | var copyGlob = Glob('*.so'); 58 | if (Platform.isWindows) { 59 | copyGlob = Glob(r'Release/*.*', caseSensitive: false); 60 | } else if (Platform.isMacOS) { 61 | copyGlob = Glob('*.dylib'); 62 | } 63 | final files = copyGlob.listSync(root: builtLibPath.path); 64 | for (var file in files) { 65 | final destPath = path.join(binDestPath.path, file.basename); 66 | logger.i(' ${file.path} => $destPath'); 67 | (file as File).copySync(destPath); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build Dart Shared Library 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | build: 9 | strategy: 10 | matrix: 11 | os: [windows-latest, ubuntu-latest, macos-13, macos-latest] 12 | include: 13 | - os: windows-latest 14 | postfix: win 15 | - os: ubuntu-latest 16 | postfix: linux 17 | - os: macos-13 18 | postfix: macos-x64 19 | - os: macos-latest 20 | postfix: macos-arm64 21 | env: 22 | DEPOT_TOOLS_WIN_TOOLCHAIN: 0 23 | continue-on-error: true 24 | runs-on: ${{ matrix.os }} 25 | steps: 26 | - if: ${{ matrix.os == 'windows-latest' }} 27 | run: git config --global core.autocrlf true 28 | - uses: actions/checkout@v3 29 | - uses: dart-lang/setup-dart@v1 30 | - uses: ilammy/msvc-dev-cmd@v1 31 | - run: dart pub get 32 | working-directory: ./scripts/build_helpers 33 | - name: Build Dart 34 | run: dart ./scripts/build_helpers/bin/build_dart.dart -v 35 | - name: Cmake 36 | uses: threeal/cmake-action@v1.3.0 37 | with: 38 | options: 39 | "BUILD_SAMPLES=OFF" 40 | - name: Build Shared Library 41 | run: cmake --build build --config release 42 | - name: Assemble artifacts 43 | run: dart ./scripts/build_helpers/bin/assemble_artifacts.dart 44 | - name: 'Upload Artifact' 45 | uses: actions/upload-artifact@v4 46 | with: 47 | name: lib-${{ matrix.postfix }} 48 | path: ./artifacts 49 | 50 | assemble_mac_dylib: 51 | runs-on: macos-latest 52 | needs: build 53 | steps: 54 | - uses: actions/download-artifact@v4 55 | with: 56 | name: lib-macos-x64 57 | path: lib-macos-x64 58 | - uses: actions/download-artifact@v4 59 | with: 60 | name: lib-macos-arm64 61 | path: lib-macos-arm64 62 | - run: | 63 | mkdir -p artifacts/bin 64 | mkdir -p artifacts/include 65 | lipo lib-macos-x64/bin/libdart_dll.dylib lib-macos-arm64/bin/libdart_dll.dylib -output artifacts/bin/libdart_dll.dylib -create 66 | cp -r lib-macos-arm64/include/* artifacts/include 67 | - name: 'Upload Artifact' 68 | uses: actions/upload-artifact@v4 69 | with: 70 | name: lib-macos 71 | path: ./artifacts 72 | -------------------------------------------------------------------------------- /dart_sdk.patch: -------------------------------------------------------------------------------- 1 | diff --git a/BUILD.gn b/BUILD.gn 2 | index fb3ca5fc537..9760a092244 100644 3 | --- a/BUILD.gn 4 | +++ b/BUILD.gn 5 | @@ -127,6 +127,10 @@ group("create_platform_sdk") { 6 | public_deps = [ "sdk:create_platform_sdk" ] 7 | } 8 | 9 | +group("libdart") { 10 | + deps = [ "runtime/bin:libdart" ] 11 | +} 12 | + 13 | group("dart2js") { 14 | import("runtime/runtime_args.gni") 15 | if (dart_target_arch != "ia32" && dart_target_arch != "x86") { 16 | diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn 17 | index b77bab8676c..b9739fab43a 100644 18 | --- a/build/config/compiler/BUILD.gn 19 | +++ b/build/config/compiler/BUILD.gn 20 | @@ -254,8 +254,9 @@ config("compiler") { 21 | cflags += [ "-fPIC" ] 22 | ldflags += [ "-fPIC" ] 23 | } else { 24 | - cflags += [ "-fPIE" ] 25 | - ldflags += [ "-fPIE" ] 26 | + # dart_shared_library overriden - we need -fPIC to properly build 27 | + cflags += [ "-fPIC" ] 28 | + ldflags += [ "-fPIC" ] 29 | } 30 | } 31 | 32 | diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn 33 | index 57d7006036d..fd677355210 100644 34 | --- a/runtime/bin/BUILD.gn 35 | +++ b/runtime/bin/BUILD.gn 36 | @@ -1224,3 +1224,47 @@ static_library("dart_embedder_runtime_jit") { 37 | output_name = "dart_embedder_runtime_jit" 38 | deps = [ ":dart_embedder_runtime_jit_set" ] 39 | } 40 | + 41 | +static_library("libdart") { 42 | + deps = [ 43 | + ":standalone_dart_io", 44 | + "..:libdart_jit", 45 | + "../platform:libdart_platform_jit", 46 | + ":dart_snapshot_cc", 47 | + ":dart_kernel_platform_cc", 48 | + "//third_party/boringssl", 49 | + "//third_party/zlib", 50 | + ] 51 | + if (dart_runtime_mode != "release") { 52 | + deps += [ "../observatory:standalone_observatory_archive" ] 53 | + } 54 | + 55 | + complete_static_lib = true 56 | + 57 | + include_dirs = [ 58 | + "..", 59 | + "//third_party", 60 | + ] 61 | + 62 | + sources = [ 63 | + "builtin.cc", 64 | + "error_exit.cc", 65 | + "error_exit.h", 66 | + "vmservice_impl.cc", 67 | + "vmservice_impl.h", 68 | + "snapshot_utils.cc", 69 | + "snapshot_utils.h", 70 | + "gzip.cc", 71 | + "gzip.h", 72 | + "dartdev_isolate.cc", 73 | + "dartdev_isolate.h", 74 | + "dfe.cc", 75 | + "dfe.h", 76 | + "loader.cc", 77 | + "loader.h", 78 | + "dart_embedder_api_impl.cc", 79 | + ] 80 | + if (dart_runtime_mode == "release") { 81 | + sources += [ "observatory_assets_empty.cc" ] 82 | + } 83 | +} 84 | -------------------------------------------------------------------------------- /scripts/build_helpers/lib/build_helpers.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | import 'package:logger/logger.dart'; 6 | import 'package:path/path.dart' as path; 7 | 8 | export 'depot_tools.dart'; 9 | 10 | class BuildToolsLogger { 11 | static Logger? _shared; 12 | static Logger get shared { 13 | _shared ??= initLogger(); 14 | return _shared!; 15 | } 16 | 17 | static Logger initLogger({Level logLevel = Level.info}) { 18 | _shared = Logger( 19 | filter: ProductionFilter(), 20 | level: logLevel, 21 | printer: SimplePrinter(), 22 | ); 23 | return shared; 24 | } 25 | } 26 | 27 | bool checkRightDirectory() { 28 | final logger = BuildToolsLogger.shared; 29 | 30 | final currentDir = Directory.current; 31 | // Check if the .dart_version and the patch file are there. 32 | if (!File(path.join(currentDir.path, '.dart_version')).existsSync()) { 33 | logger.f( 34 | 'Could not find `.dart_version`. Make sure you\'re running from the root of the `dart_dll` repo.'); 35 | return false; 36 | } 37 | 38 | if (!File(path.join(currentDir.path, 'dart_sdk.patch')).existsSync()) { 39 | logger.f( 40 | 'Could not find `dart_sdk.pathch`. Make sure you\'re running from the root of the `dart_dll` repo.'); 41 | return false; 42 | } 43 | 44 | return true; 45 | } 46 | 47 | // Waits for the process to finish, outputting output to the BuildToolsLogger 48 | Future waitForProcessFinish(Process process) async { 49 | final logger = BuildToolsLogger.shared; 50 | final stdoutCompleter = Completer(); 51 | final stderrCompleter = Completer(); 52 | 53 | process.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen( 54 | (l) { 55 | logger.i(l); 56 | }, 57 | ).onDone(() { 58 | stdoutCompleter.complete(); 59 | }); 60 | 61 | process.stderr.transform(utf8.decoder).transform(const LineSplitter()).listen( 62 | (l) { 63 | logger.i(l); 64 | }, 65 | ).onDone(() { 66 | stderrCompleter.complete(); 67 | }); 68 | 69 | var exitCode = await process.exitCode; 70 | 71 | await (Future.wait([stdoutCompleter.future, stderrCompleter.future])); 72 | 73 | return exitCode; 74 | } 75 | 76 | Future inDir(String directory, Future Function() callback) async { 77 | final oldDir = Directory.current; 78 | Directory.current = Directory(directory); 79 | final result = await callback(); 80 | Directory.current = oldDir; 81 | return result; 82 | } 83 | -------------------------------------------------------------------------------- /scripts/build_helpers/lib/depot_tools.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:path/path.dart' as path; 4 | 5 | import 'build_helpers.dart'; 6 | 7 | String? depotToolsPath; 8 | 9 | Future checkForDepotTools() async { 10 | final logger = BuildToolsLogger.shared; 11 | logger.i('Checking for depot_tools'); 12 | final result = await Process.run('gclient', ['--version'], runInShell: true); 13 | if (result.exitCode != 0) { 14 | logger.w('Failed checking for depot_tools.'); 15 | logger.d('Output of `gclient --version`:'); 16 | logger.d(result.stdout); 17 | } else { 18 | logger.i('✅ Found depot_tools'); 19 | } 20 | 21 | return result.exitCode == 0; 22 | } 23 | 24 | Future getDepotTools() async { 25 | final logger = BuildToolsLogger.shared; 26 | 27 | logger.i('Cloning depot_tools...'); 28 | final cloneResult = await Process.run( 29 | 'git', 30 | [ 31 | 'clone', 32 | 'https://chromium.googlesource.com/chromium/tools/depot_tools.git' 33 | ], 34 | runInShell: true, 35 | ); 36 | if (cloneResult.exitCode != 0) { 37 | logger.e('Failed cloning depot_tools repot.'); 38 | logger.d('Output of clone:'); 39 | logger.d(cloneResult.stdout); 40 | return false; 41 | } 42 | depotToolsPath = path.join(Directory.current.absolute.path, 'depot_tools'); 43 | 44 | logger.i('Running `gclient` to prep depot_tools.'); 45 | final gclientResult = 46 | await runAppendingDepotToolsPath('gclient', ['--version']); 47 | logger.d(gclientResult.stdout); 48 | if (gclientResult.exitCode != 0) { 49 | logger.f('Still could not run `gclient` after clone.'); 50 | return false; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | String _depotToolsAppendedPath() { 57 | final logger = BuildToolsLogger.shared; 58 | var pathEnv = Platform.environment['PATH']; 59 | 60 | if (depotToolsPath != null) { 61 | if (Platform.isWindows) { 62 | pathEnv = '$depotToolsPath;$pathEnv'; 63 | } else { 64 | pathEnv = '$depotToolsPath:$pathEnv'; 65 | } 66 | logger.d('Path is now: $pathEnv'); 67 | } 68 | 69 | return pathEnv!; 70 | } 71 | 72 | Future runAppendingDepotToolsPath( 73 | String command, List arguments) { 74 | final pathEnv = _depotToolsAppendedPath(); 75 | 76 | return Process.run( 77 | command, 78 | arguments, 79 | environment: { 80 | 'PATH': pathEnv, 81 | }, 82 | runInShell: true, 83 | ); 84 | } 85 | 86 | Future startAppendingDepotToolsPath( 87 | String command, List arguments) { 88 | final pathEnv = _depotToolsAppendedPath(); 89 | return Process.start( 90 | command, 91 | arguments, 92 | environment: {'PATH': pathEnv}, 93 | runInShell: true, 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dart Dynamic Import Library 2 | 3 | This is an attempt / POC to build the Dart VM into a dynamic library, importable in any platform Dart supports. 4 | 5 | ## Eventual support 6 | 7 | The hope is that the the dynamic library will eventually support the following targets: 8 | * A "Fully Featured" .dll / .so that supports booting Dart in different configurations: 9 | * Boot (or not) the service and kernel isolates 10 | * Support Dart Source Compilation and / or Kernel Isolates 11 | * JIT from source or .dil 12 | * An AOT Only .dll / .so 13 | 14 | Additionally we may have a static target that uses the same interface as the dynamic library, so the VM can be embedded on platforms that don't support dynamic linking. 15 | 16 | I also hope to support all platforms that Dart currently supports, plus a few extra. 17 | 18 | ## Using 19 | 20 | Lastest builds of the libraries are now available for certain targets as artifacts from [Github Actions](https://github.com/fuzzybinary/dart_shared_libray/actions) as well as all the headers necessary. 21 | 22 | Github Actions currently builds a Windows x64 `.dll`, A Linux x64 `.so`, and a macOS x64 `.dylib`. You can build a M-series dylib for macOS, but Github Actions does not currently support it. 23 | 24 | ## Building 25 | 26 | ### Prerequisets 27 | You need: 28 | * git 29 | * Dart 3+ 30 | * C++ build tools for your platform (Visual Studio, XCode, gcc, etc) 31 | * For Windows 32 | * 2019 16.61 with 10.0.20348.0 SDK don't forget install Debugger Tools 33 | * 2022 17 with ? SDK don't forget install Debugger Tools 34 | * 2017 15 with ? SDK don't forget install Debugger Tools 35 | * see dart-sdk\sdk\build\vs_toolchain.py 36 | * CMake 37 | 38 | Optionally, I recommend installing [`depot_tools`](https://www.chromium.org/developers/how-tos/depottools/) and making sure it is on your path before running setup scripts. Without depot_tools, the scripts will download them anyway, but having them already set up will save you some time with subsequent builds. 39 | 40 | ### Patching and Building Dart 41 | 42 | > NOTE: If you are building on Windows, I recommend running `.\setup_env.ps1` before executing any other scripts. 43 | > This will set up some environment variables that will be needed to build Dart properly. 44 | 45 | The first step is to build a statically linkable verison of Dart. This requires that we download Dart, patch some of the Dart build files, and then run the actual build. Thankfully there is a Dart script to do this. 46 | build_dart commandline 47 | * -v -> Verbose Log 48 | * -t -> Build Type all, release, debug 49 | 50 | ```bash 51 | cd ./scripts/build_helpers 52 | dart pub get 53 | cd ../.. 54 | dart ./scripts/build_helpers/bin/build_dart.dart 55 | ``` 56 | 57 | This script does the following: 58 | * Pulls down `depot_tools` if needed. 59 | * Clones a fresh copy of the Dart sdk git repo using `fetch` if needed. 60 | * Uses `gsync` to syncs the repo the the version of dart specificed in `.dart_version`. 61 | * Applies `dart_sdk.patch` to the repo to create the statically linkable `libdart` library 62 | * Builds `libdart` 63 | 64 | ### CMake 65 | 66 | Once Dart is built, you can use CMake to generate build files and / or build the libraries and examples 67 | 68 | ```bash 69 | cmake -B ./.build . 70 | cmake --build .\.build\ --config release 71 | ``` 72 | 73 | ### Troubleshooting 74 | 75 | #### Unresolved externals building Dart on Linux 76 | 77 | Two possible causes of this are having the snap package for Flutter installed, or having `depot_tools` not too far down your path. 78 | 79 | First, uninstall the Flutter snap package and remove the dart-sdk build directory at `dart-sdk/sdk/out` and attempt to rebuild Dart. 80 | 81 | If that doesn't work, put `depot_tools` at the front of your path, instead of at the end. Clean the dart-sdk build directory, then attempt a rebuild. -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | project(dart_dll VERSION 0.1.2) 4 | 5 | set(DARTSDK_ROOTDIR "${PROJECT_SOURCE_DIR}/../dart-sdk" CACHE FILEPATH "Directory that dart-sdk is cloned too") 6 | set(DART_DIR "${DARTSDK_ROOTDIR}/sdk") 7 | 8 | set(CMAKE_CXX_STANDARD 17) 9 | set(CMAKE_CXX_STANDARD_REQUIRED True) 10 | set(CMAKE_CXX_EXTENSIONS ) 11 | 12 | add_library(dart_dll SHARED 13 | dart_dll.cpp 14 | isolate_setup.cpp 15 | ) 16 | 17 | target_include_directories(dart_dll PRIVATE 18 | "${DART_DIR}/runtime" 19 | ) 20 | 21 | if(WIN32) 22 | set(LIB_PREFIX "lib") 23 | endif() 24 | 25 | cmake_path(ABSOLUTE_PATH DART_DIR NORMALIZE OUTPUT_VARIABLE DART_DIR) 26 | 27 | MESSAGE(STATUS "Dart SDK ${DART_DIR}") 28 | 29 | if(NOT EXISTS "${DART_DIR}/runtime/include/dart_api.h") 30 | MESSAGE(FATAL_ERROR "Missing Dart SDK or not found") 31 | endif() 32 | 33 | find_library(LIB_DART_DEBUG 34 | NAMES "${LIB_PREFIX}dart" 35 | HINTS "${DART_DIR}/out/DebugX64/obj/runtime/bin" 36 | "${DART_DIR}/xcodebuild/DebugX64/obj/runtime/bin" 37 | "${DART_DIR}/xcodebuild/DebugARM64/obj/runtime/bin" 38 | ) 39 | 40 | find_library(LIB_DART_RELEASE 41 | NAMES "${LIB_PREFIX}dart" 42 | HINTS "${DART_DIR}/out/ReleaseX64/obj/runtime/bin" 43 | "${DART_DIR}/xcodebuild/ReleaseX64/obj/runtime/bin" 44 | "${DART_DIR}/xcodebuild/ReleaseARM64/obj/runtime/bin" 45 | ) 46 | 47 | target_compile_definitions(dart_dll PRIVATE 48 | __STDC_LIMIT_MACROS 49 | __STDC_FORMAT_MACROS 50 | __STDC_CONSTANT_MACROS 51 | ) 52 | 53 | if(WIN32) 54 | target_compile_definitions(dart_dll PRIVATE 55 | _HAS_EXCEPTIONS=0 56 | _SCL_SECURE=0 57 | _SECURE_SCL=0 58 | _SCL_SECURE_NO_WARNINGS 59 | _CRT_SECURE_NO_WARNINGS 60 | _CRT_SECURE_NO_DEPRECATE 61 | DART_DLL_EXPORTING 62 | _WIN 63 | ) 64 | 65 | target_link_libraries(dart_dll 66 | dbghelp 67 | bcrypt 68 | rpcrt4 69 | ws2_32 70 | Iphlpapi 71 | Psapi 72 | shlwapi 73 | pathcch 74 | ntdll 75 | ) 76 | set_property(TARGET dart_dll PROPERTY 77 | MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 78 | elseif(LINUX) 79 | set(THREADS_PREFER_PTHREAD_FLAG ON) 80 | set(CMAKE_C_COMPILER "${DART_DIR}/buildtools/linux-x64/clang/bin/clang") 81 | set(CMAKE_CXX_COMPILER "${DART_DIR}/buildtools/linux-x64/clang/bin/clang++") 82 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib++ ${DART_DIR}/buildtools/linux-x64/clang/lib/x86_64-unknown-linux-gnu/libc++.a") 83 | #set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -stdlib=libc++ -lc++abi") 84 | 85 | find_package(Threads REQUIRED) 86 | target_link_libraries(dart_dll 87 | Threads::Threads 88 | ${CMAKE_DL_LIBS} 89 | ) 90 | elseif(APPLE) 91 | if(CMAKE_APPLE_SILICON_PROCESSOR STREQUAL "arm64") 92 | set(CMAKE_C_COMPILER "${DART_DIR}/buildtools/mac-arm64/clang/bin/clang") 93 | set(CMAKE_CXX_COMPILER "${DART_DIR}/buildtools/mac-arm64/clang/bin/clang++") 94 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib++ ${DART_DIR}/buildtools/mac-arm64/clang/lib/libc++.a -framework Cocoa -framework QuartzCore -framework Security") 95 | else() 96 | set(CMAKE_C_COMPILER "${DART_DIR}/buildtools/mac-x64/clang/bin/clang") 97 | set(CMAKE_CXX_COMPILER "${DART_DIR}/buildtools/mac-x64/clang/bin/clang++") 98 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -nostdlib++ ${DART_DIR}/buildtools/mac-x64/clang/lib/libc++.a -framework Cocoa -framework QuartzCore -framework Security") 99 | endif() 100 | endif() 101 | 102 | if(LIB_DART_DEBUG) 103 | target_link_libraries(dart_dll debug ${LIB_DART_DEBUG}) 104 | else() 105 | target_link_libraries(dart_dll debug ${LIB_DART_RELEASE}) 106 | endif() 107 | 108 | target_link_libraries(dart_dll optimized ${LIB_DART_RELEASE}) -------------------------------------------------------------------------------- /examples/realtime_example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace Cute; 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include "drawable.h" 11 | 12 | static Dart_Isolate _dart_isolate = nullptr; 13 | static unsigned int _dart_pending_messages = 0; 14 | 15 | void dart_message_notify_callback(Dart_Isolate isolate) { 16 | _dart_pending_messages++; 17 | } 18 | 19 | Dart_PersistentHandle _root_library; 20 | bool init_dart() { 21 | DartDllConfig config; 22 | DartDll_Initialize(config); 23 | 24 | //if package_config.json not exits run pub get 25 | _dart_isolate = DartDll_LoadScript("dart/main.dart", 26 | "dart/.dart_tool/package_config.json"); 27 | if (_dart_isolate == nullptr) { 28 | return false; 29 | } 30 | 31 | Dart_EnterIsolate(_dart_isolate); 32 | Dart_SetMessageNotifyCallback(dart_message_notify_callback); 33 | 34 | Dart_EnterScope(); 35 | Dart_Handle root_library = Dart_RootLibrary(); 36 | _root_library = Dart_NewPersistentHandle(root_library); 37 | Dart_Handle init_function_name = Dart_NewStringFromCString("main"); 38 | Dart_Handle result = 39 | Dart_Invoke(root_library, init_function_name, 0, nullptr); 40 | if (Dart_IsError(result)) { 41 | std::cout << Dart_GetError(result); 42 | Dart_ExitScope(); 43 | return false; 44 | } 45 | 46 | Dart_ExitScope(); 47 | 48 | return true; 49 | } 50 | 51 | void dart_frame(float delta_time) { 52 | Dart_EnterScope(); 53 | 54 | Dart_Handle frame_function_name = Dart_NewStringFromCString("frame"); 55 | Dart_Handle args[1] = { 56 | Dart_NewDouble(delta_time), 57 | }; 58 | Dart_Handle root_library = Dart_HandleFromPersistent(_root_library); 59 | Dart_Handle result = 60 | Dart_Invoke(root_library, frame_function_name, 1, args); 61 | 62 | Dart_ExitScope(); 63 | } 64 | 65 | void dart_frame_maintanance() { 66 | // Drain the Dart microtask queue. This allows Futures and async methods 67 | // to complete and execute any pending code. 68 | DartDll_DrainMicrotaskQueue(); 69 | 70 | // Handle any pending messages. This includes handling Finalizers. 71 | Dart_EnterScope(); 72 | 73 | while (_dart_pending_messages > 0) { 74 | 75 | Dart_HandleMessage(); 76 | _dart_pending_messages--; 77 | } 78 | 79 | uint64_t currentTime = Dart_TimelineGetMicros(); 80 | // Notify Dart we're likely to be idle for the next few ms, 81 | // this allows the garbage collector to fire if needed. 82 | Dart_NotifyIdle(currentTime + 3000); 83 | 84 | Dart_ExitScope(); 85 | } 86 | 87 | std::unordered_map entity_drawable_map; 88 | unsigned int next_entity_id = 1; 89 | void render_drawables() { 90 | for (const auto& pair : entity_drawable_map) { 91 | draw_push_color(pair.second->color); 92 | draw_quad_fill(make_aabb(V2(pair.second->x, pair.second->y), 93 | V2(pair.second->x + pair.second->width, 94 | pair.second->y + pair.second->height))); 95 | draw_pop_color(); 96 | } 97 | } 98 | 99 | // 100 | // Dart accessible funcitons 101 | // These need to be exposed as "C" functions and be exported 102 | // 103 | #if defined(_WIN32) 104 | #define WORM_EXPORT extern "C" __declspec(dllexport) 105 | #else 106 | #define WORM_EXPORT extern "C" __attribute__((visibility("default"))) __attribute((used)) 107 | #endif 108 | 109 | WORM_EXPORT unsigned int create_entity(int x, int y, int width, int height) { 110 | Drawable* d = new Drawable{x, y, width, height, color_blue() }; 111 | unsigned int entity_id = next_entity_id; 112 | entity_drawable_map[entity_id] = d; 113 | 114 | next_entity_id++; 115 | 116 | return entity_id; 117 | } 118 | 119 | WORM_EXPORT void destroy_entity(unsigned int entity_id) { 120 | const auto& itr = entity_drawable_map.find(entity_id); 121 | if (itr != entity_drawable_map.end()) { 122 | delete itr->second; 123 | entity_drawable_map.erase(itr); 124 | } 125 | } 126 | 127 | WORM_EXPORT Drawable* get_drawable(unsigned int entity_id) { 128 | const auto& itr = entity_drawable_map.find(entity_id); 129 | if (itr != entity_drawable_map.end()) { 130 | return itr->second; 131 | } 132 | return nullptr; 133 | } 134 | 135 | WORM_EXPORT bool get_key_just_pressed(int key_code) { 136 | return cf_key_just_pressed((CF_KeyButton)key_code); 137 | } 138 | 139 | int main(int argc, char* argv[]) { 140 | // Create a window with a resolution of 640 x 480. 141 | int options = APP_OPTIONS_WINDOW_POS_CENTERED; 142 | Result result = 143 | make_app("Fancy Window Title", 0, 0, 0, 640, 480, options, argv[0]); 144 | if (is_error(result)) return -1; 145 | 146 | if (!init_dart()) return -1; 147 | 148 | while (app_is_running()) { 149 | app_update(); 150 | 151 | dart_frame(DELTA_TIME); 152 | dart_frame_maintanance(); 153 | 154 | render_drawables(); 155 | 156 | app_draw_onto_screen(); 157 | } 158 | 159 | destroy_app(); 160 | 161 | return 0; 162 | } -------------------------------------------------------------------------------- /scripts/build_helpers/bin/build_dart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:args/args.dart'; 5 | import 'package:build_helpers/build_helpers.dart'; 6 | import 'package:logger/logger.dart'; 7 | import 'package:path/path.dart' as path; 8 | 9 | void main(List args) async { 10 | final parser = ArgParser(); 11 | parser.addFlag('verbose', abbr: 'v', help: 'enable all debug'); 12 | parser.addMultiOption( 13 | 'target', 14 | abbr: 't', 15 | help: 'Target to build (release or debug)', 16 | allowed: ['debug', 'release', 'all'], 17 | defaultsTo: ['release'], 18 | ); 19 | parser.addFlag('help', abbr: 'h'); 20 | 21 | ArgResults? argResults; 22 | try { 23 | argResults = parser.parse(args); 24 | } catch (error) { 25 | if (error is! FormatException) rethrow; 26 | print(parser.usage); 27 | exit(-1); 28 | } 29 | 30 | if (argResults['help'] == true) { 31 | print(parser.usage); 32 | return; 33 | } 34 | 35 | Level logLevel = Level.info; 36 | if (argResults['verbose'] == true) { 37 | logLevel = Level.all; 38 | } 39 | 40 | BuildToolsLogger.initLogger( 41 | logLevel: logLevel, 42 | ); 43 | 44 | var buildTargets = argResults['target'] as List; 45 | if (buildTargets.contains('all')) { 46 | buildTargets = ['debug', 'release']; 47 | } 48 | 49 | BuildToolsLogger.shared.d('Build Targets $buildTargets'); 50 | 51 | if (!checkRightDirectory()) { 52 | // Not run from root. Exit. 53 | exit(-1); 54 | } 55 | 56 | if (Platform.isWindows) { 57 | final depotToolsEnv = Platform.environment['DEPOT_TOOLS_WIN_TOOLCHAIN']; 58 | if (depotToolsEnv == null) { 59 | BuildToolsLogger.shared.e( 60 | 'DEPOT_TOOLS_WIN_TOOOLCHAIN not set! Run ./setup_env.ps1 before running this script!'); 61 | exit(-1); 62 | } 63 | final gypMsysVersion = Platform.environment['GYP_MSVS_VERSION']; 64 | final gypMsysOverridePath = Platform.environment['GYP_MSVS_OVERRIDE_PATH']; 65 | BuildToolsLogger.shared.d('GYP_MSVS_VERSION $gypMsysVersion'); 66 | BuildToolsLogger.shared.d('GYP_MSVS_OVERRIDE_PATH $gypMsysOverridePath'); 67 | } 68 | 69 | if (!await checkForDepotTools()) { 70 | if (!await getDepotTools()) { 71 | // Fatal. Can't do this without depot_tools 72 | exit(-1); 73 | } 74 | } 75 | 76 | try { 77 | if (!await _fetchOrUpdateDartSdk()) { 78 | exit(-1); 79 | } 80 | 81 | if (!await _patchDartSdk()) { 82 | exit(-1); 83 | } 84 | 85 | for (var target in buildTargets) { 86 | if (!await _buildDart(target)) { 87 | exit(-1); 88 | } 89 | } 90 | } catch (e) { 91 | BuildToolsLogger.shared.f('Caught an exception building the Dart SDK:'); 92 | BuildToolsLogger.shared.f(e); 93 | exit(-1); 94 | } 95 | } 96 | 97 | Future _fetchOrUpdateDartSdk() async { 98 | final logger = BuildToolsLogger.shared; 99 | final dartVersion = await _fetchRequestedDartVersion(); 100 | 101 | if (!Directory('dart-sdk').existsSync()) { 102 | logger.i('dart-sdk does not exist. Doing full fetch'); 103 | 104 | await Directory('dart-sdk').create(); 105 | logger.i('Temp changing global crlf'); 106 | final gitGlobalCrlfOffProcess = await Process.start( 107 | 'git', 108 | ['config', '--global', 'core.autocrlf', 'false'], 109 | runInShell: true, 110 | ); 111 | var gitGlobalCrlfOffResult = 112 | await waitForProcessFinish(gitGlobalCrlfOffProcess); 113 | if (gitGlobalCrlfOffResult != 0) return false; 114 | 115 | final fetchResult = await inDir('dart-sdk', () async { 116 | final fetchProcess = 117 | await startAppendingDepotToolsPath('fetch', ['--no-history', 'dart']); 118 | var fetchResult = await waitForProcessFinish(fetchProcess); 119 | return fetchResult; 120 | }); 121 | if (fetchResult != 0) return false; 122 | } 123 | 124 | final finalResult = await inDir(path.join('dart-sdk', 'sdk'), () async { 125 | logger.i('Checking out tag $dartVersion'); 126 | final fetchProcess = await Process.start( 127 | 'git', 128 | ['fetch', 'origin', 'refs/tags/$dartVersion:refs/tags/$dartVersion'], 129 | runInShell: true, 130 | ); 131 | var fetchResult = await waitForProcessFinish(fetchProcess); 132 | if (fetchResult != 0) return fetchResult; 133 | 134 | final checkoutProcess = await Process.start( 135 | 'git', 136 | ['checkout', '-f', 'tags/$dartVersion'], 137 | runInShell: true, 138 | ); 139 | var checkoutResult = await waitForProcessFinish(checkoutProcess); 140 | if (checkoutResult != 0) return checkoutResult; 141 | 142 | logger.i('Performing `gclient sync -D --no-history'); 143 | final syncProcess = await startAppendingDepotToolsPath( 144 | 'gclient', 145 | ['sync', '-D', '--no-history'], 146 | ); 147 | var syncResult = await waitForProcessFinish(syncProcess); 148 | if (syncResult != 0) return syncResult; 149 | 150 | logger.i('Reverting changing global crlf'); 151 | final gitGlobalCrlfOnProcess = await Process.start( 152 | 'git', 153 | ['config', '--global', 'core.autocrlf', 'true'], 154 | runInShell: true, 155 | ); 156 | var gitGlobalCrlfOnResult = 157 | await waitForProcessFinish(gitGlobalCrlfOnProcess); 158 | if (gitGlobalCrlfOnResult != 0) return false; 159 | 160 | return 0; 161 | }); 162 | 163 | return finalResult == 0; 164 | } 165 | 166 | Future _fetchRequestedDartVersion() async { 167 | final lines = await File('.dart_version').readAsLines(); 168 | for (var line in lines) { 169 | if (line.startsWith('#')) { 170 | continue; 171 | } 172 | 173 | return line; 174 | } 175 | throw Exception('Only found comments in the `.dart_version` file!'); 176 | } 177 | 178 | Future _patchDartSdk() async { 179 | final logger = BuildToolsLogger.shared; 180 | final result = await inDir('dart-sdk/sdk', () async { 181 | logger.i("Patching the Dart SDK to create libdart"); 182 | var result = await Process.run( 183 | 'git', ['apply', '--whitespace=fix', '../../dart_sdk.patch'], 184 | runInShell: true); 185 | logger.d("[patch-stdout] ${result.stdout}"); 186 | logger.d("[patch-stderr] ${result.stderr}"); 187 | logger.d('Patch result is ${result.exitCode}'); 188 | return result.exitCode; 189 | }); 190 | if (result != 0) { 191 | logger.f('Failed to apply patch.'); 192 | } 193 | 194 | return result == 0; 195 | } 196 | 197 | Future _buildDart(String buildType) async { 198 | final logger = BuildToolsLogger.shared; 199 | logger.d('starting build for $buildType'); 200 | final result = await inDir('dart-sdk/sdk', () async { 201 | logger.i("Building libdart"); 202 | var script = './tools/build.py'; 203 | var args = ['-m', buildType, 'libdart']; 204 | var command = script; 205 | if (Platform.isWindows) { 206 | command = 'python'; 207 | args.insert(0, script); 208 | } 209 | final buildProcess = await Process.start( 210 | command, 211 | args, 212 | runInShell: true, 213 | ); 214 | var buildResult = await waitForProcessFinish(buildProcess); 215 | return buildResult; 216 | }); 217 | if (result != 0) { 218 | logger.f('Failed to build dart.'); 219 | } 220 | return result == 0; 221 | } 222 | -------------------------------------------------------------------------------- /src/dart_dll.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "dart_dll.h" 4 | #include "isolate_setup.h" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace dart::bin; 18 | using namespace dart; 19 | 20 | static DartDllConfig _dart_dll_config; 21 | 22 | extern "C" { 23 | extern const uint8_t kDartVmSnapshotData[]; 24 | extern const uint8_t kDartVmSnapshotInstructions[]; 25 | } 26 | 27 | namespace dart { 28 | namespace bin { 29 | extern unsigned int observatory_assets_archive_len; 30 | extern const uint8_t* observatory_assets_archive; 31 | } // namespace bin 32 | } // namespace dart 33 | 34 | static bool FileModifiedCallback(const char* url, int64_t since) { 35 | auto path = File::UriToPath(url); 36 | if (path == nullptr) { 37 | // If it isn't a file on local disk, we don't know if it has been 38 | // modified. 39 | return true; 40 | } 41 | int64_t data[File::kStatSize]; 42 | File::Stat(nullptr, path.get(), data); 43 | if (data[File::kType] == File::kDoesNotExist) { 44 | return true; 45 | } 46 | return data[File::kModifiedTime] > since; 47 | } 48 | 49 | Dart_Handle GetVMServiceAssetsArchiveCallback() { 50 | uint8_t* decompressed = NULL; 51 | intptr_t decompressed_len = 0; 52 | Decompress(observatory_assets_archive, observatory_assets_archive_len, 53 | &decompressed, &decompressed_len); 54 | Dart_Handle tar_file = 55 | DartUtils::MakeUint8Array(decompressed, decompressed_len); 56 | // Free decompressed memory as it has been copied into a Dart array. 57 | free(decompressed); 58 | return tar_file; 59 | } 60 | 61 | static Dart_Isolate CreateIsolateGroupAndSetup(const char* script_uri, 62 | const char* main, 63 | const char* package_root, 64 | const char* package_config, 65 | Dart_IsolateFlags* flags, 66 | void* callback_data, 67 | char** error) { 68 | Dart_Isolate isolate = nullptr; 69 | 70 | if (0 == strcmp(script_uri, DART_KERNEL_ISOLATE_NAME)) { 71 | return CreateKernelIsolate(script_uri, main, package_root, package_config, 72 | flags, callback_data, error); 73 | } else if (0 == strcmp(script_uri, DART_VM_SERVICE_ISOLATE_NAME)) { 74 | return CreateVmServiceIsolate(script_uri, main, package_root, 75 | package_config, flags, callback_data, 76 | _dart_dll_config.service_port, error); 77 | } else { 78 | return CreateIsolate(false, script_uri, main, package_config, flags, 79 | callback_data, error); 80 | } 81 | 82 | return isolate; 83 | } 84 | 85 | bool OnIsolateInitialize(void** child_callback_data, char** error) { 86 | Dart_Isolate isolate = Dart_CurrentIsolate(); 87 | assert(isolate != nullptr); 88 | 89 | auto isolate_group_data = 90 | reinterpret_cast(Dart_CurrentIsolateGroupData()); 91 | 92 | auto isolate_data = new IsolateData(isolate_group_data); 93 | *child_callback_data = isolate_data; 94 | 95 | Dart_EnterScope(); 96 | 97 | const auto script_uri = isolate_group_data->script_url; 98 | // TODO 99 | /*const bool isolate_run_app_snapshot = 100 | isolate_group_data->RunFromAppSnapshot();*/ 101 | Dart_Handle result = SetupCoreLibraries(isolate, isolate_data, 102 | /*group_start=*/false, 103 | /*resolved_packages_config=*/nullptr); 104 | if (Dart_IsError(result)) goto failed; 105 | 106 | result = DartUtils::ResolveScript(Dart_NewStringFromCString(script_uri)); 107 | if (Dart_IsError(result)) goto failed; 108 | 109 | if (isolate_group_data->kernel_buffer() != nullptr) { 110 | // Various core-library parts will send requests to the Loader to resolve 111 | // relative URIs and perform other related tasks. We need Loader to be 112 | // initialized for this to work because loading from Kernel binary 113 | // bypasses normal source code loading paths that initialize it. 114 | const char* resolved_script_uri = nullptr; 115 | result = Dart_StringToCString(result, &resolved_script_uri); 116 | if (Dart_IsError(result)) goto failed; 117 | result = Loader::InitForSnapshot(resolved_script_uri, isolate_data); 118 | if (Dart_IsError(result)) goto failed; 119 | } 120 | 121 | Dart_ExitScope(); 122 | 123 | return true; 124 | 125 | failed: 126 | *error = Utils::StrDup(Dart_GetError(result)); 127 | Dart_ExitScope(); 128 | return false; 129 | } 130 | 131 | static void OnIsolateShutdown(void*, void*) { 132 | Dart_EnterScope(); 133 | Dart_Handle sticky_error = Dart_GetStickyError(); 134 | if (!Dart_IsNull(sticky_error) && !Dart_IsFatalError(sticky_error)) { 135 | std::cerr << "Error shutting down isolate: " << Dart_GetError(sticky_error) 136 | << std::endl; 137 | } 138 | Dart_ExitScope(); 139 | } 140 | 141 | extern "C" { 142 | 143 | bool DartDll_Initialize(const DartDllConfig& config) { 144 | std::cout << "Initializig Dart ---- \n"; 145 | 146 | Dart_SetVMFlags(0, nullptr); 147 | 148 | char* error = nullptr; 149 | if (!dart::embedder::InitOnce(&error)) { 150 | std::cerr << "Dart initialization failed: " << error << std::endl; 151 | return false; 152 | } 153 | 154 | // copy the configuration 155 | memcpy(&_dart_dll_config, &config, sizeof(DartDllConfig)); 156 | 157 | dfe.Init(); 158 | dfe.set_use_dfe(); 159 | dfe.set_use_incremental_compiler(true); 160 | 161 | Dart_InitializeParams params = {}; 162 | params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; 163 | params.vm_snapshot_data = kDartVmSnapshotData; 164 | params.vm_snapshot_instructions = kDartVmSnapshotInstructions; 165 | params.create_group = CreateIsolateGroupAndSetup; 166 | params.initialize_isolate = OnIsolateInitialize; 167 | params.shutdown_isolate = OnIsolateShutdown; 168 | params.cleanup_isolate = DeleteIsolateData; 169 | params.cleanup_group = DeleteIsolateGroupData; 170 | params.entropy_source = DartUtils::EntropySource; 171 | params.get_service_assets = GetVMServiceAssetsArchiveCallback; 172 | params.start_kernel_isolate = 173 | dfe.UseDartFrontend() && dfe.CanUseDartFrontend(); 174 | 175 | char* initError = Dart_Initialize(¶ms); 176 | 177 | std::cout << "Dart initialized, error was: " 178 | << (initError != nullptr ? initError : "null") << std::endl; 179 | 180 | Dart_SetFileModifiedCallback(&FileModifiedCallback); 181 | 182 | return true; 183 | } 184 | 185 | Dart_Isolate DartDll_LoadScript(const char* script_uri, 186 | const char* package_config, 187 | void* isolate_data) { 188 | Dart_IsolateFlags isolate_flags; 189 | Dart_IsolateFlagsInitialize(&isolate_flags); 190 | 191 | char* error = nullptr; 192 | Dart_Isolate isolate = CreateIsolate(true, script_uri, "main", package_config, 193 | &isolate_flags, isolate_data, &error); 194 | 195 | return isolate; 196 | } 197 | 198 | void* DartDll_GetUserIsolateData(void* isolate_group_data) { 199 | return GetUserIsolateData(isolate_group_data); 200 | } 201 | 202 | Dart_Handle DartDll_RunMain(Dart_Handle library) { 203 | Dart_Handle mainClosure = 204 | Dart_GetField(library, Dart_NewStringFromCString("main")); 205 | if (!Dart_IsClosure(mainClosure)) { 206 | std::cerr << "Unable to find 'main' in root library hello_world.dart"; 207 | return mainClosure; 208 | } 209 | 210 | // Call _startIsolate in the isolate library to enable dispatching the 211 | // initial startup message. 212 | const intptr_t kNumIsolateArgs = 2; 213 | Dart_Handle isolateArgs[2] = {mainClosure, Dart_Null()}; 214 | Dart_Handle isolateLib = 215 | Dart_LookupLibrary(Dart_NewStringFromCString("dart:isolate")); 216 | Dart_Handle result = 217 | Dart_Invoke(isolateLib, Dart_NewStringFromCString("_startMainIsolate"), 218 | kNumIsolateArgs, isolateArgs); 219 | if (Dart_IsError(result)) { 220 | std::cout << "Dart initialized, error was: " << Dart_GetError(result) 221 | << "\n" 222 | << std::endl; 223 | return result; 224 | } 225 | 226 | // Keep handling messages until the last active receive port is closed. 227 | result = Dart_RunLoop(); 228 | 229 | return result; 230 | } 231 | 232 | Dart_Handle DartDll_DrainMicrotaskQueue() { 233 | Dart_EnterScope(); 234 | 235 | // TODO: Cache looking up the dart:isolate library 236 | Dart_Handle libraryName = Dart_NewStringFromCString("dart:isolate"); 237 | Dart_Handle isolateLib = Dart_LookupLibrary(libraryName); 238 | if (Dart_IsError(isolateLib)) { 239 | std::cerr << "Error looking up 'dart:isolate' library: " 240 | << Dart_GetError(isolateLib); 241 | Dart_ExitScope(); 242 | return isolateLib; 243 | } 244 | 245 | Dart_Handle invokeName = 246 | Dart_NewStringFromCString("_runPendingImmediateCallback"); 247 | Dart_Handle result = Dart_Invoke(isolateLib, invokeName, 0, nullptr); 248 | if (Dart_IsError(result)) { 249 | std::cerr << "Error draining microtask queue: " << Dart_GetError(result); 250 | return result; 251 | } 252 | result = Dart_HandleMessage(); 253 | if (Dart_IsError(result)) { 254 | std::cerr << "Error draining microtask queue: %s" << Dart_GetError(result); 255 | return result; 256 | } 257 | 258 | Dart_ExitScope(); 259 | 260 | return result; 261 | } 262 | 263 | bool DartDll_Shutdown() { 264 | char* error = Dart_Cleanup(); 265 | if (error != nullptr) { 266 | std::cerr << "Error cleaning up Dart: " << error; 267 | return false; 268 | } 269 | 270 | dart::embedder::Cleanup(); 271 | 272 | return true; 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/isolate_setup.cpp: -------------------------------------------------------------------------------- 1 | #include "isolate_setup.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace dart::bin; 18 | 19 | extern "C" { 20 | extern const uint8_t kDartCoreIsolateSnapshotData[]; 21 | extern const uint8_t kDartCoreIsolateSnapshotInstructions[]; 22 | } 23 | 24 | namespace { 25 | class DllIsolateGroupData : public IsolateGroupData { 26 | public: 27 | DllIsolateGroupData(const char* url, 28 | const char* packages_file, 29 | AppSnapshot* app_snapshot, 30 | bool isolate_run_app_snapshot, 31 | void* callback_data = nullptr) 32 | : IsolateGroupData(url, 33 | packages_file, 34 | app_snapshot, 35 | isolate_run_app_snapshot), 36 | callback_data(callback_data) {} 37 | 38 | void* callback_data; 39 | }; 40 | } // namespace 41 | 42 | Dart_Handle SetupCoreLibraries(Dart_Isolate isolate, 43 | IsolateData* isolate_data, 44 | bool is_isolate_group_start, 45 | const char** resolved_packages_config) { 46 | auto isolate_group_data = isolate_data->isolate_group_data(); 47 | const auto packages_file = isolate_data->packages_file(); 48 | const auto script_uri = isolate_group_data->script_url; 49 | Dart_Handle result; 50 | 51 | // Prepare builtin and other core libraries for use to resolve URIs. 52 | // Set up various closures, e.g: printing, timers etc. 53 | // Set up package configuration for URI resolution. 54 | result = DartUtils::PrepareForScriptLoading(false, true); 55 | if (Dart_IsError(result)) return result; 56 | 57 | // Setup packages config if specified. 58 | result = DartUtils::SetupPackageConfig(packages_file); 59 | if (Dart_IsError(result)) return result; 60 | 61 | if (!Dart_IsNull(result) && resolved_packages_config != nullptr) { 62 | result = Dart_StringToCString(result, resolved_packages_config); 63 | if (Dart_IsError(result)) return result; 64 | 65 | if (is_isolate_group_start) { 66 | IsolateGroupData* isolate_group_data = isolate_data->isolate_group_data(); 67 | isolate_group_data->set_resolved_packages_config( 68 | *resolved_packages_config); 69 | } 70 | } 71 | 72 | result = Dart_SetEnvironmentCallback(DartUtils::EnvironmentCallback); 73 | if (Dart_IsError(result)) return result; 74 | 75 | // Setup the native resolver as the snapshot does not carry it. 76 | Builtin::SetNativeResolver(Builtin::kBuiltinLibrary); 77 | Builtin::SetNativeResolver(Builtin::kIOLibrary); 78 | Builtin::SetNativeResolver(Builtin::kCLILibrary); 79 | VmService::SetNativeResolver(); 80 | 81 | result = DartUtils::SetupIOLibrary(nullptr, script_uri, true); 82 | if (Dart_IsError(result)) return result; 83 | 84 | return result; 85 | } 86 | 87 | Dart_Isolate CreateKernelIsolate(const char* script_uri, 88 | const char* main, 89 | const char* package_root, 90 | const char* package_config, 91 | Dart_IsolateFlags* flags, 92 | void* callback_data, 93 | char** error) { 94 | const char* kernel_snapshot_uri = dfe.frontend_filename(); 95 | const char* uri = 96 | kernel_snapshot_uri != NULL ? kernel_snapshot_uri : script_uri; 97 | 98 | const uint8_t* kernel_service_buffer = nullptr; 99 | intptr_t kernel_service_buffer_size = 0; 100 | dfe.LoadKernelService(&kernel_service_buffer, &kernel_service_buffer_size); 101 | 102 | DllIsolateGroupData* isolate_group_data = 103 | new DllIsolateGroupData(uri, package_config, nullptr, false); 104 | isolate_group_data->SetKernelBufferUnowned( 105 | const_cast(kernel_service_buffer), kernel_service_buffer_size); 106 | IsolateData* isolate_data = new IsolateData(isolate_group_data); 107 | 108 | dart::embedder::IsolateCreationData data = {script_uri, main, flags, 109 | isolate_group_data, isolate_data}; 110 | 111 | Dart_Isolate isolate = dart::embedder::CreateKernelServiceIsolate( 112 | data, kernel_service_buffer, kernel_service_buffer_size, error); 113 | if (isolate == nullptr) { 114 | std::cerr << "Error creating kernel isolate: " << *error << std::endl; 115 | delete isolate_data; 116 | delete isolate_group_data; 117 | return isolate; 118 | } 119 | 120 | // Needed? 121 | Dart_EnterIsolate(isolate); 122 | Dart_EnterScope(); 123 | 124 | Dart_SetLibraryTagHandler(Loader::LibraryTagHandler); 125 | SetupCoreLibraries(isolate, isolate_data, false, nullptr); 126 | 127 | Dart_ExitScope(); 128 | Dart_ExitIsolate(); 129 | 130 | return isolate; 131 | } 132 | 133 | Dart_Isolate CreateVmServiceIsolate(const char* script_uri, 134 | const char* main, 135 | const char* package_root, 136 | const char* package_config, 137 | Dart_IsolateFlags* flags, 138 | void* callback_data, 139 | int service_port, 140 | char** error) { 141 | DllIsolateGroupData* isolate_group_data = new DllIsolateGroupData( 142 | script_uri, package_config, nullptr, false, callback_data); 143 | IsolateData* isolate_data = new IsolateData(isolate_group_data); 144 | 145 | flags->load_vmservice_library = true; 146 | const uint8_t* isolate_snapshot_data = kDartCoreIsolateSnapshotData; 147 | const uint8_t* isolate_snapshot_instructions = 148 | kDartCoreIsolateSnapshotInstructions; 149 | 150 | dart::embedder::IsolateCreationData data = {script_uri, main, flags, 151 | isolate_group_data, isolate_data}; 152 | 153 | dart::embedder::VmServiceConfiguration vm_config = { 154 | "127.0.0.1", service_port, nullptr, true, true, true}; 155 | 156 | Dart_Isolate isolate = dart::embedder::CreateVmServiceIsolate( 157 | data, vm_config, isolate_snapshot_data, isolate_snapshot_instructions, 158 | error); 159 | if (isolate == nullptr) { 160 | std::cerr << "Error creating VM Service Isolate: " << *error << std::endl; 161 | delete isolate_data; 162 | delete isolate_group_data; 163 | return nullptr; 164 | } 165 | 166 | Dart_EnterIsolate(isolate); 167 | Dart_EnterScope(); 168 | Dart_SetEnvironmentCallback(DartUtils::EnvironmentCallback); 169 | Dart_ExitScope(); 170 | Dart_ExitIsolate(); 171 | 172 | return isolate; 173 | } 174 | 175 | Dart_Isolate CreateIsolate(bool is_main_isolate, 176 | const char* script_uri, 177 | const char* name, 178 | const char* packages_config, 179 | Dart_IsolateFlags* flags, 180 | void* callback_data, 181 | char** error) { 182 | Dart_Handle result; 183 | uint8_t* kernel_buffer = nullptr; 184 | intptr_t kernel_buffer_size; 185 | AppSnapshot* app_snapshot = nullptr; 186 | 187 | bool isolate_run_app_snapshot = false; 188 | const uint8_t* isolate_snapshot_data = kDartCoreIsolateSnapshotData; 189 | const uint8_t* isolate_snapshot_instructions = 190 | kDartCoreIsolateSnapshotInstructions; 191 | 192 | if (!is_main_isolate) { 193 | app_snapshot = Snapshot::TryReadAppSnapshot(script_uri); 194 | if (app_snapshot != nullptr && app_snapshot->IsJITorAOT()) { 195 | if (app_snapshot->IsAOT()) { 196 | *error = dart::Utils::SCreate( 197 | "The uri(%s) provided to `Isolate.spawnUri()` is an " 198 | "AOT snapshot and the JIT VM cannot spawn an isolate using it.", 199 | script_uri); 200 | delete app_snapshot; 201 | return nullptr; 202 | } 203 | isolate_run_app_snapshot = true; 204 | const uint8_t* ignore_vm_snapshot_data; 205 | const uint8_t* ignore_vm_snapshot_instructions; 206 | app_snapshot->SetBuffers( 207 | &ignore_vm_snapshot_data, &ignore_vm_snapshot_instructions, 208 | &isolate_snapshot_data, &isolate_snapshot_instructions); 209 | } 210 | } 211 | 212 | if (kernel_buffer == nullptr && !isolate_run_app_snapshot) { 213 | dfe.ReadScript(script_uri, app_snapshot, &kernel_buffer, 214 | &kernel_buffer_size, /*decode_uri=*/true); 215 | } 216 | 217 | flags->null_safety = true; 218 | 219 | DllIsolateGroupData* isolate_group_data = new DllIsolateGroupData( 220 | script_uri, packages_config, nullptr, false, callback_data); 221 | isolate_group_data->SetKernelBufferNewlyOwned(kernel_buffer, 222 | kernel_buffer_size); 223 | 224 | const uint8_t* platform_kernel_buffer = nullptr; 225 | intptr_t platform_kernel_buffer_size = 0; 226 | dfe.LoadPlatform(&platform_kernel_buffer, &platform_kernel_buffer_size); 227 | if (platform_kernel_buffer == nullptr) { 228 | platform_kernel_buffer = kernel_buffer; 229 | platform_kernel_buffer_size = kernel_buffer_size; 230 | } 231 | 232 | IsolateData* isolate_data = new IsolateData(isolate_group_data); 233 | Dart_Isolate isolate = Dart_CreateIsolateGroupFromKernel( 234 | script_uri, name, platform_kernel_buffer, platform_kernel_buffer_size, 235 | flags, isolate_group_data, isolate_data, error); 236 | 237 | if (isolate == nullptr) { 238 | std::cerr << "Error creating isolate " << name << ": " << *error 239 | << std::endl; 240 | 241 | delete isolate_data; 242 | delete isolate_group_data; 243 | return nullptr; 244 | } 245 | 246 | std::cout << "Created isolate " << name << std::endl; 247 | Dart_EnterScope(); 248 | 249 | // TODO - Set Library Tag Handler, SetupCoreLibraries 250 | result = Dart_SetLibraryTagHandler(Loader::LibraryTagHandler); 251 | if (Dart_IsError(result)) { 252 | std::cerr << "Error setting LibraryTag Handler: " << *error << std::endl; 253 | Dart_ExitScope(); 254 | Dart_ShutdownIsolate(); 255 | return nullptr; 256 | } 257 | 258 | const char* resolved_packages_config = nullptr; 259 | result = SetupCoreLibraries(isolate, isolate_data, true, 260 | &resolved_packages_config); 261 | if (Dart_IsError(result)) { 262 | std::cerr << "Error setting up core libraries for isolate: " << *error 263 | << std::endl; 264 | Dart_ExitScope(); 265 | Dart_ShutdownIsolate(); 266 | return nullptr; 267 | } 268 | 269 | if (kernel_buffer == nullptr && !Dart_IsKernelIsolate(isolate)) { 270 | uint8_t* application_kernel_buffer = nullptr; 271 | intptr_t application_kernel_buffer_size = 0; 272 | int exit_code = 0; 273 | dfe.CompileAndReadScript(script_uri, &application_kernel_buffer, 274 | &application_kernel_buffer_size, error, &exit_code, 275 | resolved_packages_config, true, false); 276 | if (application_kernel_buffer == nullptr) { 277 | std::cerr << "Error reading Dart script " << script_uri << ": " << *error 278 | << std::endl; 279 | Dart_ExitScope(); 280 | Dart_ShutdownIsolate(); 281 | return nullptr; 282 | } 283 | 284 | isolate_group_data->SetKernelBufferNewlyOwned( 285 | application_kernel_buffer, application_kernel_buffer_size); 286 | kernel_buffer = application_kernel_buffer; 287 | kernel_buffer_size = application_kernel_buffer_size; 288 | } 289 | 290 | if (kernel_buffer != nullptr) { 291 | Dart_Handle result = 292 | Dart_LoadScriptFromKernel(kernel_buffer, kernel_buffer_size); 293 | if (Dart_IsError(result)) { 294 | std::cerr << "Error loading script: " << Dart_GetError(result) 295 | << std::endl; 296 | Dart_ExitScope(); 297 | Dart_ShutdownIsolate(); 298 | return nullptr; 299 | } 300 | } 301 | 302 | Dart_ExitScope(); 303 | Dart_ExitIsolate(); 304 | *error = Dart_IsolateMakeRunnable(isolate); 305 | 306 | if (*error != nullptr) { 307 | std::cerr << "Error making isolate runnable: " << error << std::endl; 308 | Dart_EnterIsolate(isolate); 309 | Dart_ShutdownIsolate(); 310 | return nullptr; 311 | } 312 | 313 | return isolate; 314 | } 315 | 316 | void* GetUserIsolateData(void* isolate_data) { 317 | if (isolate_data == nullptr) { 318 | return nullptr; 319 | } 320 | IsolateGroupData* isolate_group_data = 321 | reinterpret_cast(isolate_data)->isolate_group_data(); 322 | 323 | if (isolate_group_data == nullptr) { 324 | return nullptr; 325 | } 326 | return static_cast(isolate_group_data)->callback_data; 327 | } 328 | 329 | void DeleteIsolateData(void* raw_isolate_group_data, void* raw_isolate_data) { 330 | auto isolate_data = reinterpret_cast(raw_isolate_data); 331 | delete isolate_data; 332 | } 333 | 334 | void DeleteIsolateGroupData(void* raw_isolate_group_data) { 335 | auto isolate_group_data = 336 | reinterpret_cast(raw_isolate_group_data); 337 | delete isolate_group_data; 338 | } 339 | --------------------------------------------------------------------------------