The Dart VM C API allows you to embed the Dart Virtual Machine in C/C++ applications and create native extensions. This API is defined in the header files located in runtime/include/.
Key Concepts
Handles
Handles are the primary way to interact with Dart objects from C code. Because the garbage collector may move objects, you cannot refer to objects directly. Instead, use handles which are automatically updated by the GC.
typedef struct _Dart_Handle* Dart_Handle;
typedef Dart_Handle Dart_PersistentHandle;
typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle;
typedef struct _Dart_FinalizableHandle* Dart_FinalizableHandle;
Handle Types
- Local handles - Allocated within the current scope, deallocated when scope exits
- Persistent handles - Live for the lifetime of the isolate unless explicitly deallocated
- Weak persistent handles - Automatically set to Dart_Null when object is garbage collected
- Finalizable handles - Automatically deleted when object is garbage collected
Handles should be passed by value and never allocated on the heap. Most functions return local handles unless otherwise specified.
Isolates
Isolates are Dart’s unit of concurrency. Each isolate has its own memory and thread of control with no shared state.
typedef struct _Dart_Isolate* Dart_Isolate;
typedef struct _Dart_IsolateGroup* Dart_IsolateGroup;
Most Dart API functions require a current isolate to be set via Dart_CreateIsolateGroup() or Dart_EnterIsolate().
Error Handling
Functions return error handles when problems occur. Check for errors using Dart_IsError().
Error types:
- Api errors - API misuse (invalid arguments or context)
- Unhandled exception errors - Dart exception thrown but not caught
- Compilation errors - Compile-time errors during execution
- Fatal errors - System wants to shut down the isolate
Initialization Functions
Dart_Initialize
Dart_EXPORT DART_API_WARN_UNUSED_RESULT char* Dart_Initialize(
Dart_InitializeParams* params);
Initializes the Dart VM. Must be called before any other Dart API functions.
Initialization parameters including VM snapshot data, callbacks for isolate lifecycle, and file I/O operations.
Returns: NULL on success, error message otherwise (caller must free)
Dart_InitializeParams params = {};
params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
params.vm_snapshot_data = vm_snapshot_data;
params.vm_snapshot_instructions = vm_snapshot_instructions;
params.create_group = CreateIsolateGroupCallback;
params.shutdown_isolate = ShutdownIsolateCallback;
char* error = Dart_Initialize(¶ms);
if (error != NULL) {
fprintf(stderr, "Failed to initialize VM: %s\n", error);
free(error);
return 1;
}
Dart_Cleanup
Dart_EXPORT DART_API_WARN_UNUSED_RESULT char* Dart_Cleanup(void);
Cleanup state in the VM before process termination.
Returns: NULL on success, error message otherwise
This function must not be called on a thread created by the VM itself.
Dart_SetVMFlags
Dart_EXPORT DART_API_WARN_UNUSED_RESULT char* Dart_SetVMFlags(
int argc,
const char** argv);
Sets command line flags. Should be called before Dart_Initialize().
Number of arguments in the argv array
Array of flag strings (e.g., “—enable-asserts”)
Isolate Management
Dart_CreateIsolateGroup
Dart_EXPORT Dart_Isolate Dart_CreateIsolateGroup(
const char* script_uri,
const char* name,
const uint8_t* isolate_snapshot_data,
const uint8_t* isolate_snapshot_instructions,
Dart_IsolateFlags* flags,
void* isolate_group_data,
void* isolate_data,
char** error);
Creates a new isolate group and isolate. The new isolate becomes the current isolate.
URI of the main source file or snapshot this isolate will load
Short name for debugging (e.g., “foo.dart:main()”)
Snapshot data buffer or NULL. Must remain valid until isolate shutdown.
VM-specific flags or NULL for defaults
Embedder group data accessible via Dart_IsolateGroupData()
Embedder isolate data accessible via Dart_IsolateData()
Returns: New isolate on success, NULL on failure
Dart_CreateIsolateGroupFromKernel
Dart_EXPORT Dart_Isolate Dart_CreateIsolateGroupFromKernel(
const char* script_uri,
const char* name,
const uint8_t* kernel_buffer,
intptr_t kernel_buffer_size,
Dart_IsolateFlags* flags,
void* isolate_group_data,
void* isolate_data,
char** error);
Creates a new isolate from a Dart Kernel file.
Buffer containing kernel/DIL program. Must remain valid until isolate shutdown.
Size of the kernel buffer in bytes
Dart_EnterIsolate
Dart_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate);
Enters an isolate. Sets the current isolate to the provided isolate.
Requires there to be no current isolate. Multiple threads may not be in the same isolate at once.
Dart_ShutdownIsolate
Dart_EXPORT void Dart_ShutdownIsolate(void);
Shuts down the current isolate. After this call, the current isolate is NULL. Any current scopes will be exited and shutdown callbacks invoked.
Dart_CurrentIsolate
Dart_EXPORT Dart_Isolate Dart_CurrentIsolate(void);
Returns the current isolate or NULL if there is no current isolate.
Scope Management
Dart_EnterScope
Dart_EXPORT void Dart_EnterScope(void);
Enters a new scope. Local handles are allocated within the current scope and deallocated when the scope exits.
Dart_ExitScope
Dart_EXPORT void Dart_ExitScope(void);
Exits the current scope and deallocates all local handles allocated within it.
void ProcessDartObject(Dart_Handle obj) {
Dart_EnterScope();
// Local handles allocated here
Dart_Handle str = Dart_ToString(obj);
const char* c_str;
Dart_StringToCString(str, &c_str);
printf("Object: %s\n", c_str);
Dart_ExitScope(); // All local handles deallocated
}
Always pair Dart_EnterScope() with Dart_ExitScope(). Failure to exit scopes causes memory leaks.
Object Creation
Dart_NewInteger
Dart_EXPORT Dart_Handle Dart_NewInteger(int64_t value);
Creates a new integer object.
Dart_NewDouble
Dart_EXPORT Dart_Handle Dart_NewDouble(double value);
Creates a new double object.
Dart_NewBoolean
Dart_EXPORT Dart_Handle Dart_NewBoolean(bool value);
Creates a new boolean object.
Dart_NewStringFromCString
Dart_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str);
Creates a new string from a NULL-terminated C string (UTF-8).
Dart_NewStringFromUTF8
Dart_EXPORT Dart_Handle Dart_NewStringFromUTF8(
const uint8_t* utf8_array,
intptr_t length);
Creates a new string from UTF-8 encoded array.
UTF-8 encoded character array
Length of the array in bytes
Error Handling Functions
Dart_IsError
Dart_EXPORT bool Dart_IsError(Dart_Handle handle);
Returns true if the handle is an error handle.
Dart_GetError
Dart_EXPORT const char* Dart_GetError(Dart_Handle handle);
Gets the error message from an error handle. Returns empty string ("") if handle is valid. The returned string is scope allocated.
Dart_PropagateError
Dart_EXPORT void Dart_PropagateError(Dart_Handle handle);
Propagates an error. This function does not return on success - it uses non-local control transfer.
Dart_Handle result = Dart_Invoke(...);
if (Dart_IsError(result)) {
// Clean up resources first
FreeMyResources();
// Then propagate - this does not return
Dart_PropagateError(result);
}
Invocation Functions
Dart_Invoke
Dart_EXPORT DART_API_WARN_UNUSED_RESULT Dart_Handle Dart_Invoke(
Dart_Handle target,
Dart_Handle name,
int number_of_arguments,
Dart_Handle* arguments);
Invokes a function or method on a target object, type, or library.
An object, type, or library
Handle to a string containing the function name
Number of arguments in the arguments array
Array of argument handles
Returns: Return value on success, error handle on failure
Dart_Handle lib = Dart_RootLibrary();
Dart_Handle name = Dart_NewStringFromCString("myFunction");
Dart_Handle args[2];
args[0] = Dart_NewInteger(42);
args[1] = Dart_NewStringFromCString("hello");
Dart_Handle result = Dart_Invoke(lib, name, 2, args);
if (Dart_IsError(result)) {
fprintf(stderr, "Error: %s\n", Dart_GetError(result));
}
Dart_InvokeClosure
Dart_EXPORT DART_API_WARN_UNUSED_RESULT Dart_Handle Dart_InvokeClosure(
Dart_Handle closure,
int number_of_arguments,
Dart_Handle* arguments);
Invokes a Dart closure with the given arguments.
Persistent Handles
Dart_NewPersistentHandle
Dart_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object);
Allocates a persistent handle that lives for the lifetime of the current isolate.
Dart_DeletePersistentHandle
Dart_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);
Deallocates a persistent handle.
Always delete persistent handles when done to avoid memory leaks. These handles are NOT automatically deallocated.
Dart_NewWeakPersistentHandle
Dart_EXPORT Dart_WeakPersistentHandle Dart_NewWeakPersistentHandle(
Dart_Handle object,
void* peer,
intptr_t external_allocation_size,
Dart_HandleFinalizer callback);
Allocates a weak persistent handle with a finalizer callback.
Pointer to native object or NULL, passed to callback
Bytes allocated externally for peer (informs GC)
Function called when object is garbage collected
typedef void (*Dart_HandleFinalizer)(void* isolate_callback_data, void* peer);
void MyFinalizer(void* isolate_data, void* peer) {
MyNativeObject* obj = (MyNativeObject*)peer;
FreeMyNativeObject(obj);
}
// Create weak handle
MyNativeObject* native_obj = AllocateMyNativeObject();
Dart_WeakPersistentHandle weak = Dart_NewWeakPersistentHandle(
dart_obj,
native_obj,
sizeof(MyNativeObject),
MyFinalizer
);
Library Functions
Dart_RootLibrary
Dart_EXPORT Dart_Handle Dart_RootLibrary(void);
Returns the root library for the current isolate.
Dart_LookupLibrary
Dart_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url);
Looks up a library by URL.
Handle to a string containing the library URL
Dart_VersionString
Dart_EXPORT const char* Dart_VersionString(void);
Returns the version string for the Dart VM. Can be called without initializing the VM.
Best Practices
- Always check for errors after API calls that can fail
- Use scopes properly to manage local handle lifetime
- Clean up resources before propagating errors
- Delete persistent handles when no longer needed
- Keep snapshots valid for the lifetime of isolates that use them
- Use Dart_SetReturnValue in native functions instead of Dart_PropagateError when possible
See Also