[jnigen] Convert Kotlin's `suspend fun` to Dart's async methods (https://github.com/dart-lang/jnigen/issues/171)

Added a boolean option to jnigen.yaml called `suspend_fun_to_async`. When `true`, Kotlin suspendable functions, specified by `suspend fun`, will be converted into Dart async methods. 

A Kotlin suspendable function gets de-sugared into a continutation-passing style (CPS) method which takes a `kotlin.coroutines.Continuation` as its final argument. 

At the preprocessing stage – if the `suspend_fun_to_async` option is set – the `asyncReturnType` of each method that has `kotlin.coroutines.Continuation` as its final argument will be set so that later in the binding generation step, the return type of the method gets converted to `Future<{asyncReturnType}>`.

The generated bindings instead passes a special `PortContinuation` instance which is defined by `package:jni`. This class implements `Continuation` and gets a native send port address in its constructor. Later, when the `suspend fun` wants to resume, the `resumeWith` method of the `PortContinuation` instance gets called, which in turn calls the `_resumeWith` native method defined in `dartjni.c`. This method send the address of the object to through the send port specified.

All the boilerplate regarding the creation of `NativePort`, `PortContinuation`, awaiting responses, checking for errors and such are automatically generated by `package:jnigen`.

For native ports to work, `package:jni` provides a handy `Jni.initDLApi()` to be called first.

Example:

This:

```kotlin
public suspend fun thinkBeforeAnswering(): String
```

Gets converted to:

```dart
Future<jni.JString> thinkBeforeAnswering() async
```
diff --git a/pkgs/jni/android/build.gradle b/pkgs/jni/android/build.gradle
index 13b25f6..8900d0c 100644
--- a/pkgs/jni/android/build.gradle
+++ b/pkgs/jni/android/build.gradle
@@ -4,6 +4,7 @@
 version '1.0'
 
 buildscript {
+    ext.kotlin_version = '1.6.10'
     repositories {
         google()
         mavenCentral()
@@ -12,6 +13,7 @@
     dependencies {
         // The Android Gradle Plugin knows how to build native code with the NDK.
         classpath 'com.android.tools.build:gradle:7.1.2'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
 }
 
diff --git a/pkgs/jni/android/src/main/java/com/github/dart_lang/jni/PortContinuation.java b/pkgs/jni/android/src/main/java/com/github/dart_lang/jni/PortContinuation.java
new file mode 100644
index 0000000..eb377e4
--- /dev/null
+++ b/pkgs/jni/android/src/main/java/com/github/dart_lang/jni/PortContinuation.java
@@ -0,0 +1,42 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jni;
+
+import androidx.annotation.Keep;
+import androidx.annotation.NonNull;
+import kotlin.coroutines.Continuation;
+import kotlin.coroutines.CoroutineContext;
+import kotlinx.coroutines.Dispatchers;
+
+/// An implementation of kotlin.coroutines.Continuation which sends the address
+/// of the object to Dart through a native port.
+///
+/// This allows converting Kotlin coroutines to Dart async methods.
+/// The implementation of native void _resumeWith is located in `dartjni.c`.
+@Keep
+public class PortContinuation implements Continuation {
+  static {
+    System.loadLibrary("dartjni");
+  }
+
+  private long port;
+
+  public PortContinuation(long port) {
+    this.port = port;
+  }
+
+  @NonNull
+  @Override
+  public CoroutineContext getContext() {
+    return (CoroutineContext) Dispatchers.getIO();
+  }
+
+  @Override
+  public void resumeWith(Object o) {
+    _resumeWith(port, o);
+  }
+
+  private native void _resumeWith(long port, Object result);
+}
diff --git a/pkgs/jni/example/pubspec.lock b/pkgs/jni/example/pubspec.lock
deleted file mode 100644
index 03efa2c..0000000
--- a/pkgs/jni/example/pubspec.lock
+++ /dev/null
@@ -1,274 +0,0 @@
-# Generated by pub
-# See https://dart.dev/tools/pub/glossary#lockfile
-packages:
-  archive:
-    dependency: transitive
-    description:
-      name: archive
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.3.0"
-  args:
-    dependency: transitive
-    description:
-      name: args
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.3.1"
-  async:
-    dependency: transitive
-    description:
-      name: async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.9.0"
-  boolean_selector:
-    dependency: transitive
-    description:
-      name: boolean_selector
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  characters:
-    dependency: transitive
-    description:
-      name: characters
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.1"
-  clock:
-    dependency: transitive
-    description:
-      name: clock
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.1"
-  collection:
-    dependency: transitive
-    description:
-      name: collection
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.16.0"
-  crypto:
-    dependency: transitive
-    description:
-      name: crypto
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.2"
-  cupertino_icons:
-    dependency: "direct main"
-    description:
-      name: cupertino_icons
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.0.5"
-  fake_async:
-    dependency: transitive
-    description:
-      name: fake_async
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  ffi:
-    dependency: "direct main"
-    description:
-      name: ffi
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  file:
-    dependency: transitive
-    description:
-      name: file
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "6.1.2"
-  flutter:
-    dependency: "direct main"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_driver:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  flutter_lints:
-    dependency: "direct dev"
-    description:
-      name: flutter_lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.1"
-  flutter_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  fuchsia_remote_debug_protocol:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  integration_test:
-    dependency: "direct dev"
-    description: flutter
-    source: sdk
-    version: "0.0.0"
-  jni:
-    dependency: "direct main"
-    description:
-      path: ".."
-      relative: true
-    source: path
-    version: "0.1.1"
-  lints:
-    dependency: transitive
-    description:
-      name: lints
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.0"
-  matcher:
-    dependency: transitive
-    description:
-      name: matcher
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.12.12"
-  material_color_utilities:
-    dependency: transitive
-    description:
-      name: material_color_utilities
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.1.5"
-  meta:
-    dependency: transitive
-    description:
-      name: meta
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.0"
-  package_config:
-    dependency: transitive
-    description:
-      name: package_config
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  path:
-    dependency: transitive
-    description:
-      name: path
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.8.2"
-  platform:
-    dependency: transitive
-    description:
-      name: platform
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.1.0"
-  plugin_platform_interface:
-    dependency: transitive
-    description:
-      name: plugin_platform_interface
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.2"
-  process:
-    dependency: transitive
-    description:
-      name: process
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "4.2.4"
-  sky_engine:
-    dependency: transitive
-    description: flutter
-    source: sdk
-    version: "0.0.99"
-  source_span:
-    dependency: transitive
-    description:
-      name: source_span
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.9.0"
-  stack_trace:
-    dependency: transitive
-    description:
-      name: stack_trace
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.10.0"
-  stream_channel:
-    dependency: transitive
-    description:
-      name: stream_channel
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.0"
-  string_scanner:
-    dependency: transitive
-    description:
-      name: string_scanner
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.1.1"
-  sync_http:
-    dependency: transitive
-    description:
-      name: sync_http
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.3.1"
-  term_glyph:
-    dependency: transitive
-    description:
-      name: term_glyph
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.2.1"
-  test_api:
-    dependency: transitive
-    description:
-      name: test_api
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.12"
-  typed_data:
-    dependency: transitive
-    description:
-      name: typed_data
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "1.3.1"
-  vector_math:
-    dependency: transitive
-    description:
-      name: vector_math
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.2"
-  vm_service:
-    dependency: transitive
-    description:
-      name: vm_service
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "9.0.0"
-  webdriver:
-    dependency: transitive
-    description:
-      name: webdriver
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "3.0.0"
-sdks:
-  dart: ">=2.17.5 <3.0.0"
-  flutter: ">=2.11.0"
diff --git a/pkgs/jni/ffigen.yaml b/pkgs/jni/ffigen.yaml
index 8286855..dc1cea2 100644
--- a/pkgs/jni/ffigen.yaml
+++ b/pkgs/jni/ffigen.yaml
@@ -23,11 +23,14 @@
   rename:
     'JniType': 'JniCallType'
 functions:
-  exclude: # Exclude init functions supposed to be defined in loaded DLL, not JNI
+  exclude:
+    # Exclude init functions supposed to be defined in loaded DLL, not JNI
     - 'JNI_.*'
     - 'GetJniContext'
     - 'setJniGetters'
     - 'jni_log'
+    # This is a native function in Java. No need to call it from Dart.
+    - 'Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith'
 structs:
   exclude:
     - 'JniContext'
diff --git a/pkgs/jni/java/.gitignore b/pkgs/jni/java/.gitignore
new file mode 100644
index 0000000..9f97022
--- /dev/null
+++ b/pkgs/jni/java/.gitignore
@@ -0,0 +1 @@
+target/
\ No newline at end of file
diff --git a/pkgs/jni/java/README.md b/pkgs/jni/java/README.md
new file mode 100644
index 0000000..18a6cd3
--- /dev/null
+++ b/pkgs/jni/java/README.md
@@ -0,0 +1,4 @@
+# Jni Java
+
+This is a standalone java support which includes `PortContinuation` to support
+converting from Kotlin's `suspend fun` to Dart's `async` functions.
diff --git a/pkgs/jni/java/pom.xml b/pkgs/jni/java/pom.xml
new file mode 100644
index 0000000..62f121e
--- /dev/null
+++ b/pkgs/jni/java/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.example</groupId>
+    <artifactId>java</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <build>
+        <finalName>jni</finalName>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-stdlib</artifactId>
+            <version>1.6.20</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlinx</groupId>
+            <artifactId>kotlinx-coroutines-core</artifactId>
+            <version>1.6.4</version>
+            <type>pom</type>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortContinuation.java b/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortContinuation.java
new file mode 100644
index 0000000..2900ab1
--- /dev/null
+++ b/pkgs/jni/java/src/main/java/com/github/dart_lang/jni/PortContinuation.java
@@ -0,0 +1,38 @@
+// Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.github.dart_lang.jni;
+
+import kotlin.coroutines.Continuation;
+import kotlin.coroutines.CoroutineContext;
+import kotlinx.coroutines.Dispatchers;
+
+/// An implementation of kotlin.coroutines.Continuation which sends the address
+/// of the object to Dart through a native port.
+///
+/// This allows converting Kotlin coroutines to Dart async methods.
+/// The implementation of native void _resumeWith is located in `dartjni.c`.
+public class PortContinuation<T> implements Continuation<T> {
+  static {
+    System.loadLibrary("dartjni");
+  }
+
+  private final long port;
+
+  public PortContinuation(long port) {
+    this.port = port;
+  }
+
+  @Override
+  public CoroutineContext getContext() {
+    return (CoroutineContext) Dispatchers.getIO();
+  }
+
+  @Override
+  public void resumeWith(Object o) {
+    _resumeWith(port, o);
+  }
+
+  private native void _resumeWith(long port, Object result);
+}
diff --git a/pkgs/jni/lib/src/jarray.dart b/pkgs/jni/lib/src/jarray.dart
index e24f8ac..d1e8ac4 100644
--- a/pkgs/jni/lib/src/jarray.dart
+++ b/pkgs/jni/lib/src/jarray.dart
@@ -36,8 +36,8 @@
   ///
   /// The [length] must be a non-negative integer.
   factory JArray(JType<E> type, int length) {
-    if (type._type == JniCallType.objectType) {
-      final clazz = type._getClass();
+    if (type._type == JniCallType.objectType && type is JObjType) {
+      final clazz = (type as JObjType).getClass();
       final array = JArray<E>.fromRef(
         type,
         _accessors.newObjectArray(length, clazz.reference, nullptr).checkedRef,
diff --git a/pkgs/jni/lib/src/jni.dart b/pkgs/jni/lib/src/jni.dart
index 7d32996..b165f8e 100644
--- a/pkgs/jni/lib/src/jni.dart
+++ b/pkgs/jni/lib/src/jni.dart
@@ -4,6 +4,7 @@
 
 import 'dart:ffi';
 import 'dart:io';
+import 'dart:isolate';
 
 import 'package:ffi/ffi.dart';
 import 'package:path/path.dart';
@@ -61,6 +62,12 @@
     _dylibDir = dylibDir;
   }
 
+  static void initDLApi() {
+    // Initializing DartApiDL used for Continuations.
+    final result = _bindings.InitDartApiDL(NativeApi.initializeApiDLData);
+    assert(result == 0);
+  }
+
   /// Spawn an instance of JVM using JNI. This method should be called at the
   /// beginning of the program with appropriate options, before other isolates
   /// are spawned.
@@ -89,6 +96,7 @@
           options: jvmOptions,
           classPath: classPath,
           version: jniVersion,
+          dylibPath: dylibDir,
           ignoreUnrecognized: ignoreUnrecognized,
           allocator: arena,
         );
@@ -98,19 +106,29 @@
   static Pointer<JavaVMInitArgs> _createVMArgs({
     List<String> options = const [],
     List<String> classPath = const [],
+    String? dylibPath,
     bool ignoreUnrecognized = false,
     int version = JNI_VERSION_1_6,
     required Allocator allocator,
   }) {
     final args = allocator<JavaVMInitArgs>();
     if (options.isNotEmpty || classPath.isNotEmpty) {
-      final count = options.length + (classPath.isNotEmpty ? 1 : 0);
+      final count = options.length +
+          (dylibPath != null ? 1 : 0) +
+          (classPath.isNotEmpty ? 1 : 0);
       final optsPtr = (count != 0) ? allocator<JavaVMOption>(count) : nullptr;
       args.ref.options = optsPtr;
       for (int i = 0; i < options.length; i++) {
         optsPtr.elementAt(i).ref.optionString =
             options[i].toNativeChars(allocator);
       }
+      if (dylibPath != null) {
+        optsPtr
+                .elementAt(count - 1 - (classPath.isNotEmpty ? 1 : 0))
+                .ref
+                .optionString =
+            "-Djava.library.path=$dylibPath".toNativeChars(allocator);
+      }
       if (classPath.isNotEmpty) {
         final classPathString = classPath.join(Platform.isWindows ? ';' : ":");
         optsPtr.elementAt(count - 1).ref.optionString =
@@ -150,6 +168,11 @@
 
   static Pointer<JniAccessors> get accessors => _bindings.GetAccessors();
 
+  /// Returns a new PortContinuation.
+  static JObjectPtr newPortContinuation(ReceivePort port) {
+    return _bindings.PortContinuation__ctor(port.sendPort.nativePort).object;
+  }
+
   /// Returns current application context on Android.
   static JObjectPtr getCachedApplicationContext() {
     return _bindings.GetApplicationContext();
diff --git a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart
index 45ce03e..3ca9198 100644
--- a/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart
+++ b/pkgs/jni/lib/src/third_party/jni_bindings_generated.dart
@@ -147,6 +147,34 @@
   late final _GetCurrentActivity =
       _GetCurrentActivityPtr.asFunction<JObjectPtr Function()>();
 
+  int InitDartApiDL(
+    ffi.Pointer<ffi.Void> data,
+  ) {
+    return _InitDartApiDL(
+      data,
+    );
+  }
+
+  late final _InitDartApiDLPtr =
+      _lookup<ffi.NativeFunction<ffi.IntPtr Function(ffi.Pointer<ffi.Void>)>>(
+          'InitDartApiDL');
+  late final _InitDartApiDL =
+      _InitDartApiDLPtr.asFunction<int Function(ffi.Pointer<ffi.Void>)>();
+
+  JniResult PortContinuation__ctor(
+    int j,
+  ) {
+    return _PortContinuation__ctor(
+      j,
+    );
+  }
+
+  late final _PortContinuation__ctorPtr =
+      _lookup<ffi.NativeFunction<JniResult Function(ffi.Int64)>>(
+          'PortContinuation__ctor');
+  late final _PortContinuation__ctor =
+      _PortContinuation__ctorPtr.asFunction<JniResult Function(int)>();
+
   ffi.Pointer<GlobalJniEnv> GetGlobalEnv() {
     return _GetGlobalEnv();
   }
diff --git a/pkgs/jni/lib/src/types.dart b/pkgs/jni/lib/src/types.dart
index b1f4a77..07102e8 100644
--- a/pkgs/jni/lib/src/types.dart
+++ b/pkgs/jni/lib/src/types.dart
@@ -31,8 +31,6 @@
   int get _type;
 
   String get signature;
-
-  JniClass _getClass() => Jni.findJniClass(signature);
 }
 
 abstract class JObjType<T extends JObject> extends JType<T> {
@@ -43,4 +41,11 @@
 
   /// Creates an object from this type using the reference.
   T fromRef(Pointer<Void> ref);
+
+  JniClass getClass() {
+    if (signature.startsWith('L') && signature.endsWith(';')) {
+      return Jni.findJniClass(signature.substring(1, signature.length - 1));
+    }
+    return Jni.findJniClass(signature);
+  }
 }
diff --git a/pkgs/jni/pubspec.yaml b/pkgs/jni/pubspec.yaml
index a0c8b56..57df539 100644
--- a/pkgs/jni/pubspec.yaml
+++ b/pkgs/jni/pubspec.yaml
@@ -1,6 +1,6 @@
 name: jni
 description: Library to access JNI from dart and flutter
-version: 0.2.1
+version: 0.3.0
 repository: https://github.com/dart-lang/jnigen/tree/main/jni
 
 environment:
diff --git a/pkgs/jni/src/CMakeLists.txt b/pkgs/jni/src/CMakeLists.txt
index d0c3ee6..2cdb3f7 100644
--- a/pkgs/jni/src/CMakeLists.txt
+++ b/pkgs/jni/src/CMakeLists.txt
@@ -10,6 +10,7 @@
 add_library(jni SHARED
   "dartjni.c"
   "third_party/global_jni_env.c"
+	"include/dart_api_dl.c"
 )
 
 set_target_properties(jni PROPERTIES
diff --git a/pkgs/jni/src/dartjni.c b/pkgs/jni/src/dartjni.c
index 6673ede..6ef1dcf 100644
--- a/pkgs/jni/src/dartjni.c
+++ b/pkgs/jni/src/dartjni.c
@@ -8,6 +8,8 @@
 
 #include "dartjni.h"
 
+#include "include/dart_api_dl.h"
+
 /// Stores class and method references for obtaining exception details
 typedef struct JniExceptionMethods {
   jclass objectClass, exceptionClass, printStreamClass;
@@ -514,3 +516,38 @@
   attach_thread();
   return jniEnv;
 }
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data) {
+  return Dart_InitializeApiDL(data);
+}
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result) {
+  Dart_CObject dartPtr;
+  dartPtr.type = Dart_CObject_kInt64;
+  dartPtr.value.as_int64 = (jlong)((*env)->NewGlobalRef(env, result));
+  Dart_PostCObject_DL(port, &dartPtr);
+}
+
+// com.github.dart_lang.jni.PortContinuation
+jclass _c_PortContinuation = NULL;
+
+jmethodID _m_PortContinuation__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j) {
+  load_class_gr(&_c_PortContinuation,
+                "com/github/dart_lang/jni/PortContinuation");
+  if (_c_PortContinuation == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_PortContinuation, &_m_PortContinuation__ctor, "<init>",
+              "(J)V");
+  if (_m_PortContinuation__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_PortContinuation,
+                                         _m_PortContinuation__ctor, j);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
diff --git a/pkgs/jni/src/dartjni.h b/pkgs/jni/src/dartjni.h
index 12e38b9..39b70bd 100644
--- a/pkgs/jni/src/dartjni.h
+++ b/pkgs/jni/src/dartjni.h
@@ -260,3 +260,13 @@
   if (exception == NULL) return NULL;
   return to_global_ref(exception);
 }
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result);
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j);
diff --git a/pkgs/jni/src/include/BUILD.gn b/pkgs/jni/src/include/BUILD.gn
new file mode 100644
index 0000000..2b10262
--- /dev/null
+++ b/pkgs/jni/src/include/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import("../../sdk_args.gni")
+
+# This rule copies header files to include/
+copy("copy_headers") {
+  visibility = [ "../../sdk:copy_headers" ]
+
+  sources = [
+    "dart_api.h",
+    "dart_api_dl.c",
+    "dart_api_dl.h",
+    "dart_native_api.h",
+    "dart_tools_api.h",
+    "dart_version.h",
+    "internal/dart_api_dl_impl.h",
+  ]
+
+  outputs =
+      [ "$root_out_dir/$dart_sdk_output/include/{{source_target_relative}}" ]
+}
diff --git a/pkgs/jni/src/include/analyze_snapshot_api.h b/pkgs/jni/src/include/analyze_snapshot_api.h
new file mode 100644
index 0000000..0e68d5c
--- /dev/null
+++ b/pkgs/jni/src/include/analyze_snapshot_api.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+#ifndef RUNTIME_INCLUDE_ANALYZE_SNAPSHOT_API_H_
+#define RUNTIME_INCLUDE_ANALYZE_SNAPSHOT_API_H_
+
+#include <stdint.h>
+
+namespace dart {
+namespace snapshot_analyzer {
+typedef struct {
+  const uint8_t* vm_snapshot_data;
+  const uint8_t* vm_snapshot_instructions;
+  const uint8_t* vm_isolate_data;
+  const uint8_t* vm_isolate_instructions;
+} Dart_SnapshotAnalyzerInformation;
+
+void Dart_DumpSnapshotInformationAsJson(char** buffer,
+                                        intptr_t* buffer_length,
+                                        Dart_SnapshotAnalyzerInformation* info);
+
+void Dart_DumpSnapshotInformationPP(Dart_SnapshotAnalyzerInformation* info);
+
+}  // namespace snapshot_analyzer
+}  // namespace dart
+
+#endif  // RUNTIME_INCLUDE_ANALYZE_SNAPSHOT_API_H_
diff --git a/pkgs/jni/src/include/bin/dart_io_api.h b/pkgs/jni/src/include/bin/dart_io_api.h
new file mode 100644
index 0000000..cc64797
--- /dev/null
+++ b/pkgs/jni/src/include/bin/dart_io_api.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef RUNTIME_INCLUDE_BIN_DART_IO_API_H_
+#define RUNTIME_INCLUDE_BIN_DART_IO_API_H_
+
+#include "dart_tools_api.h"
+
+namespace dart {
+namespace bin {
+
+// Bootstraps 'dart:io'.
+void BootstrapDartIo();
+
+// Cleans up 'dart:io'.
+void CleanupDartIo();
+
+// Lets dart:io know where the system temporary directory is located.
+// Currently only wired up on Android.
+void SetSystemTempDirectory(const char* system_temp);
+
+// Tells the system whether to capture Stdout events.
+void SetCaptureStdout(bool value);
+
+// Tells the system whether to capture Stderr events.
+void SetCaptureStderr(bool value);
+
+// Should Stdout events be captured?
+bool ShouldCaptureStdout();
+
+// Should Stderr events be captured?
+bool ShouldCaptureStderr();
+
+// Set the executable name used by Platform.executable.
+void SetExecutableName(const char* executable_name);
+
+// Set the arguments used by Platform.executableArguments.
+void SetExecutableArguments(int script_index, char** argv);
+
+// Set dart:io implementation specific fields of Dart_EmbedderInformation.
+void GetIOEmbedderInformation(Dart_EmbedderInformation* info);
+
+// Appropriate to assign to Dart_InitializeParams.file_open/read/write/close.
+void* OpenFile(const char* name, bool write);
+void ReadFile(uint8_t** data, intptr_t* file_len, void* stream);
+void WriteFile(const void* buffer, intptr_t num_bytes, void* stream);
+void CloseFile(void* stream);
+
+// Generates 'length' random bytes into 'buffer'. Returns true on success
+// and false on failure. This is appropriate to assign to
+// Dart_InitializeParams.entropy_source.
+bool GetEntropy(uint8_t* buffer, intptr_t length);
+
+// Performs a lookup of the I/O Dart_NativeFunction with a specified 'name' and
+// 'argument_count'. Returns NULL if no I/O native function with a matching
+// name and parameter count is found.
+Dart_NativeFunction LookupIONative(Dart_Handle name,
+                                   int argument_count,
+                                   bool* auto_setup_scope);
+
+// Returns the symbol for I/O native function 'nf'. Returns NULL if 'nf' is not
+// a valid I/O native function.
+const uint8_t* LookupIONativeSymbol(Dart_NativeFunction nf);
+
+}  // namespace bin
+}  // namespace dart
+
+#endif  // RUNTIME_INCLUDE_BIN_DART_IO_API_H_
diff --git a/pkgs/jni/src/include/dart_api.h b/pkgs/jni/src/include/dart_api.h
new file mode 100644
index 0000000..741447a
--- /dev/null
+++ b/pkgs/jni/src/include/dart_api.h
@@ -0,0 +1,4098 @@
+/*
+ * Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+#ifndef RUNTIME_INCLUDE_DART_API_H_
+#define RUNTIME_INCLUDE_DART_API_H_
+
+/** \mainpage Dart Embedding API Reference
+ *
+ * This reference describes the Dart Embedding API, which is used to embed the
+ * Dart Virtual Machine within C/C++ applications.
+ *
+ * This reference is generated from the header include/dart_api.h.
+ */
+
+/* __STDC_FORMAT_MACROS has to be defined before including <inttypes.h> to
+ * enable platform independent printf format specifiers. */
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+#define DART_EXTERN_C extern "C"
+#else
+#define DART_EXTERN_C extern
+#endif
+
+#if defined(__CYGWIN__)
+#error Tool chain and platform not supported.
+#elif defined(_WIN32)
+#if defined(DART_SHARED_LIB)
+#define DART_EXPORT DART_EXTERN_C __declspec(dllexport)
+#else
+#define DART_EXPORT DART_EXTERN_C
+#endif
+#else
+#if __GNUC__ >= 4
+#if defined(DART_SHARED_LIB)
+#define DART_EXPORT                                                            \
+  DART_EXTERN_C __attribute__((visibility("default"))) __attribute((used))
+#else
+#define DART_EXPORT DART_EXTERN_C
+#endif
+#else
+#error Tool chain not supported.
+#endif
+#endif
+
+#if __GNUC__
+#define DART_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#elif _MSC_VER
+#define DART_WARN_UNUSED_RESULT _Check_return_
+#else
+#define DART_WARN_UNUSED_RESULT
+#endif
+
+/*
+ * =======
+ * Handles
+ * =======
+ */
+
+/**
+ * An isolate is the unit of concurrency in Dart. Each isolate has
+ * its own memory and thread of control. No state is shared between
+ * isolates. Instead, isolates communicate by message passing.
+ *
+ * Each thread keeps track of its current isolate, which is the
+ * isolate which is ready to execute on the current thread. The
+ * current isolate may be NULL, in which case no isolate is ready to
+ * execute. Most of the Dart apis require there to be a current
+ * isolate in order to function without error. The current isolate is
+ * set by any call to Dart_CreateIsolateGroup or Dart_EnterIsolate.
+ */
+typedef struct _Dart_Isolate* Dart_Isolate;
+typedef struct _Dart_IsolateGroup* Dart_IsolateGroup;
+
+/**
+ * An object reference managed by the Dart VM garbage collector.
+ *
+ * Because the garbage collector may move objects, it is unsafe to
+ * refer to objects directly. Instead, we refer to objects through
+ * handles, which are known to the garbage collector and updated
+ * automatically when the object is moved. Handles should be passed
+ * by value (except in cases like out-parameters) and should never be
+ * allocated on the heap.
+ *
+ * Most functions in the Dart Embedding API return a handle. When a
+ * function completes normally, this will be a valid handle to an
+ * object in the Dart VM heap. This handle may represent the result of
+ * the operation or it may be a special valid handle used merely to
+ * indicate successful completion. Note that a valid handle may in
+ * some cases refer to the null object.
+ *
+ * --- Error handles ---
+ *
+ * When a function encounters a problem that prevents it from
+ * completing normally, it returns an error handle (See Dart_IsError).
+ * An error handle has an associated error message that gives more
+ * details about the problem (See Dart_GetError).
+ *
+ * There are four kinds of error handles that can be produced,
+ * depending on what goes wrong:
+ *
+ * - Api error handles are produced when an api function is misused.
+ *   This happens when a Dart embedding api function is called with
+ *   invalid arguments or in an invalid context.
+ *
+ * - Unhandled exception error handles are produced when, during the
+ *   execution of Dart code, an exception is thrown but not caught.
+ *   Prototypically this would occur during a call to Dart_Invoke, but
+ *   it can occur in any function which triggers the execution of Dart
+ *   code (for example, Dart_ToString).
+ *
+ *   An unhandled exception error provides access to an exception and
+ *   stacktrace via the functions Dart_ErrorGetException and
+ *   Dart_ErrorGetStackTrace.
+ *
+ * - Compilation error handles are produced when, during the execution
+ *   of Dart code, a compile-time error occurs.  As above, this can
+ *   occur in any function which triggers the execution of Dart code.
+ *
+ * - Fatal error handles are produced when the system wants to shut
+ *   down the current isolate.
+ *
+ * --- Propagating errors ---
+ *
+ * When an error handle is returned from the top level invocation of
+ * Dart code in a program, the embedder must handle the error as they
+ * see fit.  Often, the embedder will print the error message produced
+ * by Dart_Error and exit the program.
+ *
+ * When an error is returned while in the body of a native function,
+ * it can be propagated up the call stack by calling
+ * Dart_PropagateError, Dart_SetReturnValue, or Dart_ThrowException.
+ * Errors should be propagated unless there is a specific reason not
+ * to.  If an error is not propagated then it is ignored.  For
+ * example, if an unhandled exception error is ignored, that
+ * effectively "catches" the unhandled exception.  Fatal errors must
+ * always be propagated.
+ *
+ * When an error is propagated, any current scopes created by
+ * Dart_EnterScope will be exited.
+ *
+ * Using Dart_SetReturnValue to propagate an exception is somewhat
+ * more convenient than using Dart_PropagateError, and should be
+ * preferred for reasons discussed below.
+ *
+ * Dart_PropagateError and Dart_ThrowException do not return.  Instead
+ * they transfer control non-locally using a setjmp-like mechanism.
+ * This can be inconvenient if you have resources that you need to
+ * clean up before propagating the error.
+ *
+ * When relying on Dart_PropagateError, we often return error handles
+ * rather than propagating them from helper functions.  Consider the
+ * following contrived example:
+ *
+ * 1    Dart_Handle isLongStringHelper(Dart_Handle arg) {
+ * 2      intptr_t* length = 0;
+ * 3      result = Dart_StringLength(arg, &length);
+ * 4      if (Dart_IsError(result)) {
+ * 5        return result;
+ * 6      }
+ * 7      return Dart_NewBoolean(length > 100);
+ * 8    }
+ * 9
+ * 10   void NativeFunction_isLongString(Dart_NativeArguments args) {
+ * 11     Dart_EnterScope();
+ * 12     AllocateMyResource();
+ * 13     Dart_Handle arg = Dart_GetNativeArgument(args, 0);
+ * 14     Dart_Handle result = isLongStringHelper(arg);
+ * 15     if (Dart_IsError(result)) {
+ * 16       FreeMyResource();
+ * 17       Dart_PropagateError(result);
+ * 18       abort();  // will not reach here
+ * 19     }
+ * 20     Dart_SetReturnValue(result);
+ * 21     FreeMyResource();
+ * 22     Dart_ExitScope();
+ * 23   }
+ *
+ * In this example, we have a native function which calls a helper
+ * function to do its work.  On line 5, the helper function could call
+ * Dart_PropagateError, but that would not give the native function a
+ * chance to call FreeMyResource(), causing a leak.  Instead, the
+ * helper function returns the error handle to the caller, giving the
+ * caller a chance to clean up before propagating the error handle.
+ *
+ * When an error is propagated by calling Dart_SetReturnValue, the
+ * native function will be allowed to complete normally and then the
+ * exception will be propagated only once the native call
+ * returns. This can be convenient, as it allows the C code to clean
+ * up normally.
+ *
+ * The example can be written more simply using Dart_SetReturnValue to
+ * propagate the error.
+ *
+ * 1    Dart_Handle isLongStringHelper(Dart_Handle arg) {
+ * 2      intptr_t* length = 0;
+ * 3      result = Dart_StringLength(arg, &length);
+ * 4      if (Dart_IsError(result)) {
+ * 5        return result
+ * 6      }
+ * 7      return Dart_NewBoolean(length > 100);
+ * 8    }
+ * 9
+ * 10   void NativeFunction_isLongString(Dart_NativeArguments args) {
+ * 11     Dart_EnterScope();
+ * 12     AllocateMyResource();
+ * 13     Dart_Handle arg = Dart_GetNativeArgument(args, 0);
+ * 14     Dart_SetReturnValue(isLongStringHelper(arg));
+ * 15     FreeMyResource();
+ * 16     Dart_ExitScope();
+ * 17   }
+ *
+ * In this example, the call to Dart_SetReturnValue on line 14 will
+ * either return the normal return value or the error (potentially
+ * generated on line 3).  The call to FreeMyResource on line 15 will
+ * execute in either case.
+ *
+ * --- Local and persistent handles ---
+ *
+ * Local handles are allocated within the current scope (see
+ * Dart_EnterScope) and go away when the current scope exits. Unless
+ * otherwise indicated, callers should assume that all functions in
+ * the Dart embedding api return local handles.
+ *
+ * Persistent handles are allocated within the current isolate. They
+ * can be used to store objects across scopes. Persistent handles have
+ * the lifetime of the current isolate unless they are explicitly
+ * deallocated (see Dart_DeletePersistentHandle).
+ * The type Dart_Handle represents a handle (both local and persistent).
+ * The type Dart_PersistentHandle is a Dart_Handle and it is used to
+ * document that a persistent handle is expected as a parameter to a call
+ * or the return value from a call is a persistent handle.
+ *
+ * FinalizableHandles are persistent handles which are auto deleted when
+ * the object is garbage collected. It is never safe to use these handles
+ * unless you know the object is still reachable.
+ *
+ * WeakPersistentHandles are persistent handles which are automatically set
+ * to point Dart_Null when the object is garbage collected. They are not auto
+ * deleted, so it is safe to use them after the object has become unreachable.
+ */
+typedef struct _Dart_Handle* Dart_Handle;
+typedef Dart_Handle Dart_PersistentHandle;
+typedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle;
+typedef struct _Dart_FinalizableHandle* Dart_FinalizableHandle;
+// These structs are versioned by DART_API_DL_MAJOR_VERSION, bump the
+// version when changing this struct.
+
+typedef void (*Dart_HandleFinalizer)(void* isolate_callback_data, void* peer);
+
+/**
+ * Is this an error handle?
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT bool Dart_IsError(Dart_Handle handle);
+
+/**
+ * Is this an api error handle?
+ *
+ * Api error handles are produced when an api function is misused.
+ * This happens when a Dart embedding api function is called with
+ * invalid arguments or in an invalid context.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT bool Dart_IsApiError(Dart_Handle handle);
+
+/**
+ * Is this an unhandled exception error handle?
+ *
+ * Unhandled exception error handles are produced when, during the
+ * execution of Dart code, an exception is thrown but not caught.
+ * This can occur in any function which triggers the execution of Dart
+ * code.
+ *
+ * See Dart_ErrorGetException and Dart_ErrorGetStackTrace.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle handle);
+
+/**
+ * Is this a compilation error handle?
+ *
+ * Compilation error handles are produced when, during the execution
+ * of Dart code, a compile-time error occurs.  This can occur in any
+ * function which triggers the execution of Dart code.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT bool Dart_IsCompilationError(Dart_Handle handle);
+
+/**
+ * Is this a fatal error handle?
+ *
+ * Fatal error handles are produced when the system wants to shut down
+ * the current isolate.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT bool Dart_IsFatalError(Dart_Handle handle);
+
+/**
+ * Gets the error message from an error handle.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \return A C string containing an error message if the handle is
+ *   error. An empty C string ("") if the handle is valid. This C
+ *   String is scope allocated and is only valid until the next call
+ *   to Dart_ExitScope.
+*/
+DART_EXPORT const char* Dart_GetError(Dart_Handle handle);
+
+/**
+ * Is this an error handle for an unhandled exception?
+ */
+DART_EXPORT bool Dart_ErrorHasException(Dart_Handle handle);
+
+/**
+ * Gets the exception Object from an unhandled exception error handle.
+ */
+DART_EXPORT Dart_Handle Dart_ErrorGetException(Dart_Handle handle);
+
+/**
+ * Gets the stack trace Object from an unhandled exception error handle.
+ */
+DART_EXPORT Dart_Handle Dart_ErrorGetStackTrace(Dart_Handle handle);
+
+/**
+ * Produces an api error handle with the provided error message.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \param error the error message.
+ */
+DART_EXPORT Dart_Handle Dart_NewApiError(const char* error);
+DART_EXPORT Dart_Handle Dart_NewCompilationError(const char* error);
+
+/**
+ * Produces a new unhandled exception error handle.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \param exception An instance of a Dart object to be thrown or
+ *        an ApiError or CompilationError handle.
+ *        When an ApiError or CompilationError handle is passed in
+ *        a string object of the error message is created and it becomes
+ *        the Dart object to be thrown.
+ */
+DART_EXPORT Dart_Handle Dart_NewUnhandledExceptionError(Dart_Handle exception);
+
+/**
+ * Propagates an error.
+ *
+ * If the provided handle is an unhandled exception error, this
+ * function will cause the unhandled exception to be rethrown.  This
+ * will proceed in the standard way, walking up Dart frames until an
+ * appropriate 'catch' block is found, executing 'finally' blocks,
+ * etc.
+ *
+ * If the error is not an unhandled exception error, we will unwind
+ * the stack to the next C frame.  Intervening Dart frames will be
+ * discarded; specifically, 'finally' blocks will not execute.  This
+ * is the standard way that compilation errors (and the like) are
+ * handled by the Dart runtime.
+ *
+ * In either case, when an error is propagated any current scopes
+ * created by Dart_EnterScope will be exited.
+ *
+ * See the additional discussion under "Propagating Errors" at the
+ * beginning of this file.
+ *
+ * \param handle An error handle (See Dart_IsError)
+ *
+ * On success, this function does not return.  On failure, the
+ * process is terminated.
+ */
+DART_EXPORT void Dart_PropagateError(Dart_Handle handle);
+
+/**
+ * Converts an object to a string.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \return The converted string if no error occurs during
+ *   the conversion. If an error does occur, an error handle is
+ *   returned.
+ */
+DART_EXPORT Dart_Handle Dart_ToString(Dart_Handle object);
+
+/**
+ * Checks to see if two handles refer to identically equal objects.
+ *
+ * If both handles refer to instances, this is equivalent to using the top-level
+ * function identical() from dart:core. Otherwise, returns whether the two
+ * argument handles refer to the same object.
+ *
+ * \param obj1 An object to be compared.
+ * \param obj2 An object to be compared.
+ *
+ * \return True if the objects are identically equal.  False otherwise.
+ */
+DART_EXPORT bool Dart_IdentityEquals(Dart_Handle obj1, Dart_Handle obj2);
+
+/**
+ * Allocates a handle in the current scope from a persistent handle.
+ */
+DART_EXPORT Dart_Handle Dart_HandleFromPersistent(Dart_PersistentHandle object);
+
+/**
+ * Allocates a handle in the current scope from a weak persistent handle.
+ *
+ * This will be a handle to Dart_Null if the object has been garbage collected.
+ */
+DART_EXPORT Dart_Handle
+Dart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object);
+
+/**
+ * Allocates a persistent handle for an object.
+ *
+ * This handle has the lifetime of the current isolate unless it is
+ * explicitly deallocated by calling Dart_DeletePersistentHandle.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object);
+
+/**
+ * Assign value of local handle to a persistent handle.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \param obj1 A persistent handle whose value needs to be set.
+ * \param obj2 An object whose value needs to be set to the persistent handle.
+ */
+DART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1,
+                                          Dart_Handle obj2);
+
+/**
+ * Deallocates a persistent handle.
+ *
+ * Requires there to be a current isolate group.
+ */
+DART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);
+
+/**
+ * Allocates a weak persistent handle for an object.
+ *
+ * This handle has the lifetime of the current isolate. The handle can also be
+ * explicitly deallocated by calling Dart_DeleteWeakPersistentHandle.
+ *
+ * If the object becomes unreachable the callback is invoked with the peer as
+ * argument. The callback can be executed on any thread, will have a current
+ * isolate group, but will not have a current isolate. The callback can only
+ * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle. This
+ * gives the embedder the ability to cleanup data associated with the object.
+ * The handle will point to the Dart_Null object after the finalizer has been
+ * run. It is illegal to call into the VM with any other Dart_* functions from
+ * the callback. If the handle is deleted before the object becomes
+ * unreachable, the callback is never invoked.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \param object An object with identity.
+ * \param peer A pointer to a native object or NULL.  This value is
+ *   provided to callback when it is invoked.
+ * \param external_allocation_size The number of externally allocated
+ *   bytes for peer. Used to inform the garbage collector.
+ * \param callback A function pointer that will be invoked sometime
+ *   after the object is garbage collected, unless the handle has been deleted.
+ *   A valid callback needs to be specified it cannot be NULL.
+ *
+ * \return The weak persistent handle or NULL. NULL is returned in case of bad
+ *   parameters.
+ */
+DART_EXPORT Dart_WeakPersistentHandle
+Dart_NewWeakPersistentHandle(Dart_Handle object,
+                             void* peer,
+                             intptr_t external_allocation_size,
+                             Dart_HandleFinalizer callback);
+
+/**
+ * Deletes the given weak persistent [object] handle.
+ *
+ * Requires there to be a current isolate group.
+ */
+DART_EXPORT void Dart_DeleteWeakPersistentHandle(
+    Dart_WeakPersistentHandle object);
+
+/**
+ * Updates the external memory size for the given weak persistent handle.
+ *
+ * May trigger garbage collection.
+ */
+DART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,
+                                         intptr_t external_allocation_size);
+
+/**
+ * Allocates a finalizable handle for an object.
+ *
+ * This handle has the lifetime of the current isolate group unless the object
+ * pointed to by the handle is garbage collected, in this case the VM
+ * automatically deletes the handle after invoking the callback associated
+ * with the handle. The handle can also be explicitly deallocated by
+ * calling Dart_DeleteFinalizableHandle.
+ *
+ * If the object becomes unreachable the callback is invoked with the
+ * the peer as argument. The callback can be executed on any thread, will have
+ * an isolate group, but will not have a current isolate. The callback can only
+ * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle.
+ * This gives the embedder the ability to cleanup data associated with the
+ * object and clear out any cached references to the handle. All references to
+ * this handle after the callback will be invalid. It is illegal to call into
+ * the VM with any other Dart_* functions from the callback. If the handle is
+ * deleted before the object becomes unreachable, the callback is never
+ * invoked.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \param object An object with identity.
+ * \param peer A pointer to a native object or NULL.  This value is
+ *   provided to callback when it is invoked.
+ * \param external_allocation_size The number of externally allocated
+ *   bytes for peer. Used to inform the garbage collector.
+ * \param callback A function pointer that will be invoked sometime
+ *   after the object is garbage collected, unless the handle has been deleted.
+ *   A valid callback needs to be specified it cannot be NULL.
+ *
+ * \return The finalizable handle or NULL. NULL is returned in case of bad
+ *   parameters.
+ */
+DART_EXPORT Dart_FinalizableHandle
+Dart_NewFinalizableHandle(Dart_Handle object,
+                          void* peer,
+                          intptr_t external_allocation_size,
+                          Dart_HandleFinalizer callback);
+
+/**
+ * Deletes the given finalizable [object] handle.
+ *
+ * The caller has to provide the actual Dart object the handle was created from
+ * to prove the object (and therefore the finalizable handle) is still alive.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT void Dart_DeleteFinalizableHandle(Dart_FinalizableHandle object,
+                                              Dart_Handle strong_ref_to_object);
+
+/**
+ * Updates the external memory size for the given finalizable handle.
+ *
+ * The caller has to provide the actual Dart object the handle was created from
+ * to prove the object (and therefore the finalizable handle) is still alive.
+ *
+ * May trigger garbage collection.
+ */
+DART_EXPORT void Dart_UpdateFinalizableExternalSize(
+    Dart_FinalizableHandle object,
+    Dart_Handle strong_ref_to_object,
+    intptr_t external_allocation_size);
+
+/*
+ * ==========================
+ * Initialization and Globals
+ * ==========================
+ */
+
+/**
+ * Gets the version string for the Dart VM.
+ *
+ * The version of the Dart VM can be accessed without initializing the VM.
+ *
+ * \return The version string for the embedded Dart VM.
+ */
+DART_EXPORT const char* Dart_VersionString(void);
+
+/**
+ * Isolate specific flags are set when creating a new isolate using the
+ * Dart_IsolateFlags structure.
+ *
+ * Current version of flags is encoded in a 32-bit integer with 16 bits used
+ * for each part.
+ */
+
+#define DART_FLAGS_CURRENT_VERSION (0x0000000c)
+
+typedef struct {
+  int32_t version;
+  bool enable_asserts;
+  bool use_field_guards;
+  bool use_osr;
+  bool obfuscate;
+  bool load_vmservice_library;
+  bool copy_parent_code;
+  bool null_safety;
+  bool is_system_isolate;
+  bool snapshot_is_dontneed_safe;
+  bool branch_coverage;
+} Dart_IsolateFlags;
+
+/**
+ * Initialize Dart_IsolateFlags with correct version and default values.
+ */
+DART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags);
+
+/**
+ * An isolate creation and initialization callback function.
+ *
+ * This callback, provided by the embedder, is called when the VM
+ * needs to create an isolate. The callback should create an isolate
+ * by calling Dart_CreateIsolateGroup and load any scripts required for
+ * execution.
+ *
+ * This callback may be called on a different thread than the one
+ * running the parent isolate.
+ *
+ * When the function returns NULL, it is the responsibility of this
+ * function to ensure that Dart_ShutdownIsolate has been called if
+ * required (for example, if the isolate was created successfully by
+ * Dart_CreateIsolateGroup() but the root library fails to load
+ * successfully, then the function should call Dart_ShutdownIsolate
+ * before returning).
+ *
+ * When the function returns NULL, the function should set *error to
+ * a malloc-allocated buffer containing a useful error message.  The
+ * caller of this function (the VM) will make sure that the buffer is
+ * freed.
+ *
+ * \param script_uri The uri of the main source file or snapshot to load.
+ *   Either the URI of the parent isolate set in Dart_CreateIsolateGroup for
+ *   Isolate.spawn, or the argument to Isolate.spawnUri canonicalized by the
+ *   library tag handler of the parent isolate.
+ *   The callback is responsible for loading the program by a call to
+ *   Dart_LoadScriptFromKernel.
+ * \param main The name of the main entry point this isolate will
+ *   eventually run.  This is provided for advisory purposes only to
+ *   improve debugging messages.  The main function is not invoked by
+ *   this function.
+ * \param package_root Ignored.
+ * \param package_config Uri of the package configuration file (either in format
+ *   of .packages or .dart_tool/package_config.json) for this isolate
+ *   to resolve package imports against. If this parameter is not passed the
+ *   package resolution of the parent isolate should be used.
+ * \param flags Default flags for this isolate being spawned. Either inherited
+ *   from the spawning isolate or passed as parameters when spawning the
+ *   isolate from Dart code.
+ * \param isolate_data The isolate data which was passed to the
+ *   parent isolate when it was created by calling Dart_CreateIsolateGroup().
+ * \param error A structure into which the embedder can place a
+ *   C string containing an error message in the case of failures.
+ *
+ * \return The embedder returns NULL if the creation and
+ *   initialization was not successful and the isolate if successful.
+ */
+typedef Dart_Isolate (*Dart_IsolateGroupCreateCallback)(
+    const char* script_uri,
+    const char* main,
+    const char* package_root,
+    const char* package_config,
+    Dart_IsolateFlags* flags,
+    void* isolate_data,
+    char** error);
+
+/**
+ * An isolate initialization callback function.
+ *
+ * This callback, provided by the embedder, is called when the VM has created an
+ * isolate within an existing isolate group (i.e. from the same source as an
+ * existing isolate).
+ *
+ * The callback should setup native resolvers and might want to set a custom
+ * message handler via [Dart_SetMessageNotifyCallback] and mark the isolate as
+ * runnable.
+ *
+ * This callback may be called on a different thread than the one
+ * running the parent isolate.
+ *
+ * When the function returns `false`, it is the responsibility of this
+ * function to ensure that `Dart_ShutdownIsolate` has been called.
+ *
+ * When the function returns `false`, the function should set *error to
+ * a malloc-allocated buffer containing a useful error message.  The
+ * caller of this function (the VM) will make sure that the buffer is
+ * freed.
+ *
+ * \param child_isolate_data The callback data to associate with the new
+ *        child isolate.
+ * \param error A structure into which the embedder can place a
+ *   C string containing an error message in the case the initialization fails.
+ *
+ * \return The embedder returns true if the initialization was successful and
+ *         false otherwise (in which case the VM will terminate the isolate).
+ */
+typedef bool (*Dart_InitializeIsolateCallback)(void** child_isolate_data,
+                                               char** error);
+
+/**
+ * An isolate shutdown callback function.
+ *
+ * This callback, provided by the embedder, is called before the vm
+ * shuts down an isolate.  The isolate being shutdown will be the current
+ * isolate. It is safe to run Dart code.
+ *
+ * This function should be used to dispose of native resources that
+ * are allocated to an isolate in order to avoid leaks.
+ *
+ * \param isolate_group_data The same callback data which was passed to the
+ *   isolate group when it was created.
+ * \param isolate_data The same callback data which was passed to the isolate
+ *   when it was created.
+ */
+typedef void (*Dart_IsolateShutdownCallback)(void* isolate_group_data,
+                                             void* isolate_data);
+
+/**
+ * An isolate cleanup callback function.
+ *
+ * This callback, provided by the embedder, is called after the vm
+ * shuts down an isolate. There will be no current isolate and it is *not*
+ * safe to run Dart code.
+ *
+ * This function should be used to dispose of native resources that
+ * are allocated to an isolate in order to avoid leaks.
+ *
+ * \param isolate_group_data The same callback data which was passed to the
+ *   isolate group when it was created.
+ * \param isolate_data The same callback data which was passed to the isolate
+ *   when it was created.
+ */
+typedef void (*Dart_IsolateCleanupCallback)(void* isolate_group_data,
+                                            void* isolate_data);
+
+/**
+ * An isolate group cleanup callback function.
+ *
+ * This callback, provided by the embedder, is called after the vm
+ * shuts down an isolate group.
+ *
+ * This function should be used to dispose of native resources that
+ * are allocated to an isolate in order to avoid leaks.
+ *
+ * \param isolate_group_data The same callback data which was passed to the
+ *   isolate group when it was created.
+ *
+ */
+typedef void (*Dart_IsolateGroupCleanupCallback)(void* isolate_group_data);
+
+/**
+ * A thread start callback function.
+ * This callback, provided by the embedder, is called after a thread in the
+ * vm thread pool starts.
+ * This function could be used to adjust thread priority or attach native
+ * resources to the thread.
+ */
+typedef void (*Dart_ThreadStartCallback)(void);
+
+/**
+ * A thread death callback function.
+ * This callback, provided by the embedder, is called before a thread in the
+ * vm thread pool exits.
+ * This function could be used to dispose of native resources that
+ * are associated and attached to the thread, in order to avoid leaks.
+ */
+typedef void (*Dart_ThreadExitCallback)(void);
+
+/**
+ * Opens a file for reading or writing.
+ *
+ * Callback provided by the embedder for file operations. If the
+ * embedder does not allow file operations this callback can be
+ * NULL.
+ *
+ * \param name The name of the file to open.
+ * \param write A boolean variable which indicates if the file is to
+ *   opened for writing. If there is an existing file it needs to truncated.
+ */
+typedef void* (*Dart_FileOpenCallback)(const char* name, bool write);
+
+/**
+ * Read contents of file.
+ *
+ * Callback provided by the embedder for file operations. If the
+ * embedder does not allow file operations this callback can be
+ * NULL.
+ *
+ * \param data Buffer allocated in the callback into which the contents
+ *   of the file are read into. It is the responsibility of the caller to
+ *   free this buffer.
+ * \param file_length A variable into which the length of the file is returned.
+ *   In the case of an error this value would be -1.
+ * \param stream Handle to the opened file.
+ */
+typedef void (*Dart_FileReadCallback)(uint8_t** data,
+                                      intptr_t* file_length,
+                                      void* stream);
+
+/**
+ * Write data into file.
+ *
+ * Callback provided by the embedder for file operations. If the
+ * embedder does not allow file operations this callback can be
+ * NULL.
+ *
+ * \param data Buffer which needs to be written into the file.
+ * \param length Length of the buffer.
+ * \param stream Handle to the opened file.
+ */
+typedef void (*Dart_FileWriteCallback)(const void* data,
+                                       intptr_t length,
+                                       void* stream);
+
+/**
+ * Closes the opened file.
+ *
+ * Callback provided by the embedder for file operations. If the
+ * embedder does not allow file operations this callback can be
+ * NULL.
+ *
+ * \param stream Handle to the opened file.
+ */
+typedef void (*Dart_FileCloseCallback)(void* stream);
+
+typedef bool (*Dart_EntropySource)(uint8_t* buffer, intptr_t length);
+
+/**
+ * Callback provided by the embedder that is used by the vmservice isolate
+ * to request the asset archive. The asset archive must be an uncompressed tar
+ * archive that is stored in a Uint8List.
+ *
+ * If the embedder has no vmservice isolate assets, the callback can be NULL.
+ *
+ * \return The embedder must return a handle to a Uint8List containing an
+ *   uncompressed tar archive or null.
+ */
+typedef Dart_Handle (*Dart_GetVMServiceAssetsArchive)(void);
+
+/**
+ * The current version of the Dart_InitializeFlags. Should be incremented every
+ * time Dart_InitializeFlags changes in a binary incompatible way.
+ */
+#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000007)
+
+/** Forward declaration */
+struct Dart_CodeObserver;
+
+/**
+ * Callback provided by the embedder that is used by the VM to notify on code
+ * object creation, *before* it is invoked the first time.
+ * This is useful for embedders wanting to e.g. keep track of PCs beyond
+ * the lifetime of the garbage collected code objects.
+ * Note that an address range may be used by more than one code object over the
+ * lifecycle of a process. Clients of this function should record timestamps for
+ * these compilation events and when collecting PCs to disambiguate reused
+ * address ranges.
+ */
+typedef void (*Dart_OnNewCodeCallback)(struct Dart_CodeObserver* observer,
+                                       const char* name,
+                                       uintptr_t base,
+                                       uintptr_t size);
+
+typedef struct Dart_CodeObserver {
+  void* data;
+
+  Dart_OnNewCodeCallback on_new_code;
+} Dart_CodeObserver;
+
+/**
+ * Optional callback provided by the embedder that is used by the VM to
+ * implement registration of kernel blobs for the subsequent Isolate.spawnUri
+ * If no callback is provided, the registration of kernel blobs will throw
+ * an error.
+ * 
+ * \param kernel_buffer A buffer which contains a kernel program. Callback
+ *                      should copy the contents of `kernel_buffer` as
+ *                      it may be freed immediately after registration.
+ * \param kernel_buffer_size The size of `kernel_buffer`.
+ *
+ * \return A C string representing URI which can be later used
+ *         to spawn a new isolate. This C String should be scope allocated
+ *         or owned by the embedder.
+ *         Returns NULL if embedder runs out of memory.
+ */
+typedef const char* (*Dart_RegisterKernelBlobCallback)(
+    const uint8_t* kernel_buffer,
+    intptr_t kernel_buffer_size);
+
+/**
+ * Optional callback provided by the embedder that is used by the VM to
+ * unregister kernel blobs.
+ * If no callback is provided, the unregistration of kernel blobs will throw
+ * an error.
+ * 
+ * \param kernel_blob_uri URI of the kernel blob to unregister.
+ */
+typedef void (*Dart_UnregisterKernelBlobCallback)(const char* kernel_blob_uri);
+
+/**
+ * Describes how to initialize the VM. Used with Dart_Initialize.
+ */
+typedef struct {
+  /**
+   * Identifies the version of the struct used by the client.
+   * should be initialized to DART_INITIALIZE_PARAMS_CURRENT_VERSION.
+   */
+  int32_t version;
+
+  /**
+   * A buffer containing snapshot data, or NULL if no snapshot is provided.
+   *
+   * If provided, the buffer must remain valid until Dart_Cleanup returns.
+   */
+  const uint8_t* vm_snapshot_data;
+
+  /**
+   * A buffer containing a snapshot of precompiled instructions, or NULL if
+   * no snapshot is provided.
+   *
+   * If provided, the buffer must remain valid until Dart_Cleanup returns.
+   */
+  const uint8_t* vm_snapshot_instructions;
+
+  /**
+   * A function to be called during isolate group creation.
+   * See Dart_IsolateGroupCreateCallback.
+   */
+  Dart_IsolateGroupCreateCallback create_group;
+
+  /**
+   * A function to be called during isolate
+   * initialization inside an existing isolate group.
+   * See Dart_InitializeIsolateCallback.
+   */
+  Dart_InitializeIsolateCallback initialize_isolate;
+
+  /**
+   * A function to be called right before an isolate is shutdown.
+   * See Dart_IsolateShutdownCallback.
+   */
+  Dart_IsolateShutdownCallback shutdown_isolate;
+
+  /**
+   * A function to be called after an isolate was shutdown.
+   * See Dart_IsolateCleanupCallback.
+   */
+  Dart_IsolateCleanupCallback cleanup_isolate;
+
+  /**
+   * A function to be called after an isolate group is
+   * shutdown. See Dart_IsolateGroupCleanupCallback.
+   */
+  Dart_IsolateGroupCleanupCallback cleanup_group;
+
+  Dart_ThreadStartCallback thread_start;
+  Dart_ThreadExitCallback thread_exit;
+  Dart_FileOpenCallback file_open;
+  Dart_FileReadCallback file_read;
+  Dart_FileWriteCallback file_write;
+  Dart_FileCloseCallback file_close;
+  Dart_EntropySource entropy_source;
+
+  /**
+   * A function to be called by the service isolate when it requires the
+   * vmservice assets archive. See Dart_GetVMServiceAssetsArchive.
+   */
+  Dart_GetVMServiceAssetsArchive get_service_assets;
+
+  bool start_kernel_isolate;
+
+  /**
+   * An external code observer callback function. The observer can be invoked
+   * as early as during the Dart_Initialize() call.
+   */
+  Dart_CodeObserver* code_observer;
+
+  /**
+   * Kernel blob registration callback function. See Dart_RegisterKernelBlobCallback.
+   */
+  Dart_RegisterKernelBlobCallback register_kernel_blob;
+
+  /**
+   * Kernel blob unregistration callback function. See Dart_UnregisterKernelBlobCallback.
+   */
+  Dart_UnregisterKernelBlobCallback unregister_kernel_blob;
+} Dart_InitializeParams;
+
+/**
+ * Initializes the VM.
+ *
+ * \param params A struct containing initialization information. The version
+ *   field of the struct must be DART_INITIALIZE_PARAMS_CURRENT_VERSION.
+ *
+ * \return NULL if initialization is successful. Returns an error message
+ *   otherwise. The caller is responsible for freeing the error message.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Initialize(
+    Dart_InitializeParams* params);
+
+/**
+ * Cleanup state in the VM before process termination.
+ *
+ * \return NULL if cleanup is successful. Returns an error message otherwise.
+ *   The caller is responsible for freeing the error message.
+ *
+ * NOTE: This function must not be called on a thread that was created by the VM
+ * itself.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Cleanup(void);
+
+/**
+ * Sets command line flags. Should be called before Dart_Initialize.
+ *
+ * \param argc The length of the arguments array.
+ * \param argv An array of arguments.
+ *
+ * \return NULL if successful. Returns an error message otherwise.
+ *  The caller is responsible for freeing the error message.
+ *
+ * NOTE: This call does not store references to the passed in c-strings.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_SetVMFlags(int argc,
+                                                          const char** argv);
+
+/**
+ * Returns true if the named VM flag is of boolean type, specified, and set to
+ * true.
+ *
+ * \param flag_name The name of the flag without leading punctuation
+ *                  (example: "enable_asserts").
+ */
+DART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name);
+
+/*
+ * ========
+ * Isolates
+ * ========
+ */
+
+/**
+ * Creates a new isolate. The new isolate becomes the current isolate.
+ *
+ * A snapshot can be used to restore the VM quickly to a saved state
+ * and is useful for fast startup. If snapshot data is provided, the
+ * isolate will be started using that snapshot data. Requires a core snapshot or
+ * an app snapshot created by Dart_CreateSnapshot or
+ * Dart_CreatePrecompiledSnapshot* from a VM with the same version.
+ *
+ * Requires there to be no current isolate.
+ *
+ * \param script_uri The main source file or snapshot this isolate will load.
+ *   The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a
+ *   child isolate is created by Isolate.spawn. The embedder should use a URI
+ *   that allows it to load the same program into such a child isolate.
+ * \param name A short name for the isolate to improve debugging messages.
+ *   Typically of the format 'foo.dart:main()'.
+ * \param isolate_snapshot_data Buffer containing the snapshot data of the
+ *   isolate or NULL if no snapshot is provided. If provided, the buffer must
+ *   remain valid until the isolate shuts down.
+ * \param isolate_snapshot_instructions Buffer containing the snapshot
+ *   instructions of the isolate or NULL if no snapshot is provided. If
+ *   provided, the buffer must remain valid until the isolate shuts down.
+ * \param flags Pointer to VM specific flags or NULL for default flags.
+ * \param isolate_group_data Embedder group data. This data can be obtained
+ *   by calling Dart_IsolateGroupData and will be passed to the
+ *   Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and
+ *   Dart_IsolateGroupCleanupCallback.
+ * \param isolate_data Embedder data.  This data will be passed to
+ *   the Dart_IsolateGroupCreateCallback when new isolates are spawned from
+ *   this parent isolate.
+ * \param error Returns NULL if creation is successful, an error message
+ *   otherwise. The caller is responsible for calling free() on the error
+ *   message.
+ *
+ * \return The new isolate on success, or NULL if isolate creation failed.
+ */
+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 inside the isolate group of [group_member].
+ *
+ * Requires there to be no current isolate.
+ *
+ * \param group_member An isolate from the same group into which the newly created
+ *   isolate should be born into. Other threads may not have entered / enter this
+ *   member isolate.
+ * \param name A short name for the isolate for debugging purposes.
+ * \param shutdown_callback A callback to be called when the isolate is being
+ *   shutdown (may be NULL).
+ * \param cleanup_callback A callback to be called when the isolate is being
+ *   cleaned up (may be NULL).
+ * \param isolate_data The embedder-specific data associated with this isolate.
+ * \param error Set to NULL if creation is successful, set to an error
+ *   message otherwise. The caller is responsible for calling free() on the
+ *   error message.
+ *
+ * \return The newly created isolate on success, or NULL if isolate creation
+ *   failed.
+ *
+ * If successful, the newly created isolate will become the current isolate.
+ */
+DART_EXPORT Dart_Isolate
+Dart_CreateIsolateInGroup(Dart_Isolate group_member,
+                          const char* name,
+                          Dart_IsolateShutdownCallback shutdown_callback,
+                          Dart_IsolateCleanupCallback cleanup_callback,
+                          void* child_isolate_data,
+                          char** error);
+
+/* TODO(turnidge): Document behavior when there is already a current
+ * isolate. */
+
+/**
+ * Creates a new isolate from a Dart Kernel file. The new isolate
+ * becomes the current isolate.
+ *
+ * Requires there to be no current isolate.
+ *
+ * \param script_uri The main source file or snapshot this isolate will load.
+ *   The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a
+ * child isolate is created by Isolate.spawn. The embedder should use a URI that
+ *   allows it to load the same program into such a child isolate.
+ * \param name A short name for the isolate to improve debugging messages.
+ *   Typically of the format 'foo.dart:main()'.
+ * \param kernel_buffer A buffer which contains a kernel/DIL program. Must
+ *   remain valid until isolate shutdown.
+ * \param kernel_buffer_size The size of `kernel_buffer`.
+ * \param flags Pointer to VM specific flags or NULL for default flags.
+ * \param isolate_group_data Embedder group data. This data can be obtained
+ *   by calling Dart_IsolateGroupData and will be passed to the
+ *   Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and
+ *   Dart_IsolateGroupCleanupCallback.
+ * \param isolate_data Embedder data.  This data will be passed to
+ *   the Dart_IsolateGroupCreateCallback when new isolates are spawned from
+ *   this parent isolate.
+ * \param error Returns NULL if creation is successful, an error message
+ *   otherwise. The caller is responsible for calling free() on the error
+ *   message.
+ *
+ * \return The new isolate on success, or NULL if isolate creation failed.
+ */
+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);
+/**
+ * Shuts down the current isolate. After this call, the current isolate is NULL.
+ * Any current scopes created by Dart_EnterScope will be exited. Invokes the
+ * shutdown callback and any callbacks of remaining weak persistent handles.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT void Dart_ShutdownIsolate(void);
+/* TODO(turnidge): Document behavior when there is no current isolate. */
+
+/**
+ * Returns the current isolate. Will return NULL if there is no
+ * current isolate.
+ */
+DART_EXPORT Dart_Isolate Dart_CurrentIsolate(void);
+
+/**
+ * Returns the callback data associated with the current isolate. This
+ * data was set when the isolate got created or initialized.
+ */
+DART_EXPORT void* Dart_CurrentIsolateData(void);
+
+/**
+ * Returns the callback data associated with the given isolate. This
+ * data was set when the isolate got created or initialized.
+ */
+DART_EXPORT void* Dart_IsolateData(Dart_Isolate isolate);
+
+/**
+ * Returns the current isolate group. Will return NULL if there is no
+ * current isolate group.
+ */
+DART_EXPORT Dart_IsolateGroup Dart_CurrentIsolateGroup(void);
+
+/**
+ * Returns the callback data associated with the current isolate group. This
+ * data was passed to the isolate group when it was created.
+ */
+DART_EXPORT void* Dart_CurrentIsolateGroupData(void);
+
+/**
+ * Gets an id that uniquely identifies current isolate group.
+ *
+ * It is the responsibility of the caller to free the returned ID.
+ */
+typedef int64_t Dart_IsolateGroupId;
+DART_EXPORT Dart_IsolateGroupId Dart_CurrentIsolateGroupId();
+
+/**
+ * Returns the callback data associated with the specified isolate group. This
+ * data was passed to the isolate when it was created.
+ * The embedder is responsible for ensuring the consistency of this data
+ * with respect to the lifecycle of an isolate group.
+ */
+DART_EXPORT void* Dart_IsolateGroupData(Dart_Isolate isolate);
+
+/**
+ * Returns the debugging name for the current isolate.
+ *
+ * This name is unique to each isolate and should only be used to make
+ * debugging messages more comprehensible.
+ */
+DART_EXPORT Dart_Handle Dart_DebugName(void);
+
+/**
+ * Returns the ID for an isolate which is used to query the service protocol.
+ *
+ * It is the responsibility of the caller to free the returned ID.
+ */
+DART_EXPORT const char* Dart_IsolateServiceId(Dart_Isolate isolate);
+
+/**
+ * Enters an isolate. After calling this function,
+ * the current isolate will be set to the provided isolate.
+ *
+ * Requires there to be no current isolate. Multiple threads may not be in
+ * the same isolate at once.
+ */
+DART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate);
+
+/**
+ * Kills the given isolate.
+ *
+ * This function has the same effect as dart:isolate's
+ * Isolate.kill(priority:immediate).
+ * It can interrupt ordinary Dart code but not native code. If the isolate is
+ * in the middle of a long running native function, the isolate will not be
+ * killed until control returns to Dart.
+ *
+ * Does not require a current isolate. It is safe to kill the current isolate if
+ * there is one.
+ */
+DART_EXPORT void Dart_KillIsolate(Dart_Isolate isolate);
+
+/**
+ * Notifies the VM that the embedder expects to be idle until |deadline|. The VM
+ * may use this time to perform garbage collection or other tasks to avoid
+ * delays during execution of Dart code in the future.
+ *
+ * |deadline| is measured in microseconds against the system's monotonic time.
+ * This clock can be accessed via Dart_TimelineGetMicros().
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT void Dart_NotifyIdle(int64_t deadline);
+
+/**
+ * Notifies the VM that the embedder expects the application's working set has
+ * recently shrunk significantly and is not expected to rise in the near future.
+ * The VM may spend O(heap-size) time performing clean up work.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT void Dart_NotifyDestroyed(void);
+
+/**
+ * Notifies the VM that the system is running low on memory.
+ *
+ * Does not require a current isolate. Only valid after calling Dart_Initialize.
+ */
+DART_EXPORT void Dart_NotifyLowMemory(void);
+
+typedef enum {
+  /**
+   * Balanced
+   */
+  Dart_PerformanceMode_Default,
+  /**
+   * Optimize for low latency, at the expense of throughput and memory overhead
+   * by performing work in smaller batches (requiring more overhead) or by
+   * delaying work (requiring more memory). An embedder should not remain in
+   * this mode indefinitely.
+   */
+  Dart_PerformanceMode_Latency,
+  /**
+   * Optimize for high throughput, at the expense of latency and memory overhead
+   * by performing work in larger batches with more intervening growth.
+   */
+  Dart_PerformanceMode_Throughput,
+  /**
+   * Optimize for low memory, at the expensive of throughput and latency by more
+   * frequently performing work.
+   */
+  Dart_PerformanceMode_Memory,
+} Dart_PerformanceMode;
+
+/**
+ * Set the desired performance trade-off.
+ *
+ * Requires a current isolate.
+ *
+ * Returns the previous performance mode.
+ */
+DART_EXPORT Dart_PerformanceMode
+Dart_SetPerformanceMode(Dart_PerformanceMode mode);
+
+/**
+ * Starts the CPU sampling profiler.
+ */
+DART_EXPORT void Dart_StartProfiling(void);
+
+/**
+ * Stops the CPU sampling profiler.
+ *
+ * Note that some profile samples might still be taken after this fucntion
+ * returns due to the asynchronous nature of the implementation on some
+ * platforms.
+ */
+DART_EXPORT void Dart_StopProfiling(void);
+
+/**
+ * Notifies the VM that the current thread should not be profiled until a
+ * matching call to Dart_ThreadEnableProfiling is made.
+ *
+ * NOTE: By default, if a thread has entered an isolate it will be profiled.
+ * This function should be used when an embedder knows a thread is about
+ * to make a blocking call and wants to avoid unnecessary interrupts by
+ * the profiler.
+ */
+DART_EXPORT void Dart_ThreadDisableProfiling(void);
+
+/**
+ * Notifies the VM that the current thread should be profiled.
+ *
+ * NOTE: It is only legal to call this function *after* calling
+ *   Dart_ThreadDisableProfiling.
+ *
+ * NOTE: By default, if a thread has entered an isolate it will be profiled.
+ */
+DART_EXPORT void Dart_ThreadEnableProfiling(void);
+
+/**
+ * Register symbol information for the Dart VM's profiler and crash dumps.
+ *
+ * This consumes the output of //topaz/runtime/dart/profiler_symbols, which
+ * should be treated as opaque.
+ */
+DART_EXPORT void Dart_AddSymbols(const char* dso_name,
+                                 void* buffer,
+                                 intptr_t buffer_size);
+
+/**
+ * Exits an isolate. After this call, Dart_CurrentIsolate will
+ * return NULL.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT void Dart_ExitIsolate(void);
+/* TODO(turnidge): We don't want users of the api to be able to exit a
+ * "pure" dart isolate. Implement and document. */
+
+/**
+ * Creates a full snapshot of the current isolate heap.
+ *
+ * A full snapshot is a compact representation of the dart vm isolate heap
+ * and dart isolate heap states. These snapshots are used to initialize
+ * the vm isolate on startup and fast initialization of an isolate.
+ * A Snapshot of the heap is created before any dart code has executed.
+ *
+ * Requires there to be a current isolate. Not available in the precompiled
+ * runtime (check Dart_IsPrecompiledRuntime).
+ *
+ * \param vm_snapshot_data_buffer Returns a pointer to a buffer containing the
+ *   vm snapshot. This buffer is scope allocated and is only valid
+ *   until the next call to Dart_ExitScope.
+ * \param vm_snapshot_data_size Returns the size of vm_snapshot_data_buffer.
+ * \param isolate_snapshot_data_buffer Returns a pointer to a buffer containing
+ *   the isolate snapshot. This buffer is scope allocated and is only valid
+ *   until the next call to Dart_ExitScope.
+ * \param isolate_snapshot_data_size Returns the size of
+ *   isolate_snapshot_data_buffer.
+ * \param is_core Create a snapshot containing core libraries.
+ *   Such snapshot should be agnostic to null safety mode.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer,
+                    intptr_t* vm_snapshot_data_size,
+                    uint8_t** isolate_snapshot_data_buffer,
+                    intptr_t* isolate_snapshot_data_size,
+                    bool is_core);
+
+/**
+ * Returns whether the buffer contains a kernel file.
+ *
+ * \param buffer Pointer to a buffer that might contain a kernel binary.
+ * \param buffer_size Size of the buffer.
+ *
+ * \return Whether the buffer contains a kernel binary (full or partial).
+ */
+DART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size);
+
+/**
+ * Make isolate runnable.
+ *
+ * When isolates are spawned, this function is used to indicate that
+ * the creation and initialization (including script loading) of the
+ * isolate is complete and the isolate can start.
+ * This function expects there to be no current isolate.
+ *
+ * \param isolate The isolate to be made runnable.
+ *
+ * \return NULL if successful. Returns an error message otherwise. The caller
+ * is responsible for freeing the error message.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_IsolateMakeRunnable(
+    Dart_Isolate isolate);
+
+/*
+ * ==================
+ * Messages and Ports
+ * ==================
+ */
+
+/**
+ * A port is used to send or receive inter-isolate messages
+ */
+typedef int64_t Dart_Port;
+
+/**
+ * ILLEGAL_PORT is a port number guaranteed never to be associated with a valid
+ * port.
+ */
+#define ILLEGAL_PORT ((Dart_Port)0)
+
+/**
+ * A message notification callback.
+ *
+ * This callback allows the embedder to provide a custom wakeup mechanism for
+ * the delivery of inter-isolate messages. This function is called once per
+ * message on an arbitrary thread. It is the responsibility of the embedder to
+ * eventually call Dart_HandleMessage once per callback received with the
+ * destination isolate set as the current isolate to process the message.
+ */
+typedef void (*Dart_MessageNotifyCallback)(Dart_Isolate destination_isolate);
+
+/**
+ * Allows embedders to provide a custom wakeup mechanism for the delivery of
+ * inter-isolate messages. This setting only applies to the current isolate.
+ *
+ * This mechanism is optional: if not provided, the isolate will be scheduled on
+ * a VM-managed thread pool. An embedder should provide this callback if it
+ * wants to run an isolate on a specific thread or to interleave handling of
+ * inter-isolate messages with other event sources.
+ *
+ * Most embedders will only call this function once, before isolate
+ * execution begins. If this function is called after isolate
+ * execution begins, the embedder is responsible for threading issues.
+ */
+DART_EXPORT void Dart_SetMessageNotifyCallback(
+    Dart_MessageNotifyCallback message_notify_callback);
+/* TODO(turnidge): Consider moving this to isolate creation so that it
+ * is impossible to mess up. */
+
+/**
+ * Query the current message notify callback for the isolate.
+ *
+ * \return The current message notify callback for the isolate.
+ */
+DART_EXPORT Dart_MessageNotifyCallback Dart_GetMessageNotifyCallback(void);
+
+/**
+ * The VM's default message handler supports pausing an isolate before it
+ * processes the first message and right after the it processes the isolate's
+ * final message. This can be controlled for all isolates by two VM flags:
+ *
+ *   `--pause-isolates-on-start`
+ *   `--pause-isolates-on-exit`
+ *
+ * Additionally, Dart_SetShouldPauseOnStart and Dart_SetShouldPauseOnExit can be
+ * used to control this behaviour on a per-isolate basis.
+ *
+ * When an embedder is using a Dart_MessageNotifyCallback the embedder
+ * needs to cooperate with the VM so that the service protocol can report
+ * accurate information about isolates and so that tools such as debuggers
+ * work reliably.
+ *
+ * The following functions can be used to implement pausing on start and exit.
+ */
+
+/**
+ * If the VM flag `--pause-isolates-on-start` was passed this will be true.
+ *
+ * \return A boolean value indicating if pause on start was requested.
+ */
+DART_EXPORT bool Dart_ShouldPauseOnStart(void);
+
+/**
+ * Override the VM flag `--pause-isolates-on-start` for the current isolate.
+ *
+ * \param should_pause Should the isolate be paused on start?
+ *
+ * NOTE: This must be called before Dart_IsolateMakeRunnable.
+ */
+DART_EXPORT void Dart_SetShouldPauseOnStart(bool should_pause);
+
+/**
+ * Is the current isolate paused on start?
+ *
+ * \return A boolean value indicating if the isolate is paused on start.
+ */
+DART_EXPORT bool Dart_IsPausedOnStart(void);
+
+/**
+ * Called when the embedder has paused the current isolate on start and when
+ * the embedder has resumed the isolate.
+ *
+ * \param paused Is the isolate paused on start?
+ */
+DART_EXPORT void Dart_SetPausedOnStart(bool paused);
+
+/**
+ * If the VM flag `--pause-isolates-on-exit` was passed this will be true.
+ *
+ * \return A boolean value indicating if pause on exit was requested.
+ */
+DART_EXPORT bool Dart_ShouldPauseOnExit(void);
+
+/**
+ * Override the VM flag `--pause-isolates-on-exit` for the current isolate.
+ *
+ * \param should_pause Should the isolate be paused on exit?
+ *
+ */
+DART_EXPORT void Dart_SetShouldPauseOnExit(bool should_pause);
+
+/**
+ * Is the current isolate paused on exit?
+ *
+ * \return A boolean value indicating if the isolate is paused on exit.
+ */
+DART_EXPORT bool Dart_IsPausedOnExit(void);
+
+/**
+ * Called when the embedder has paused the current isolate on exit and when
+ * the embedder has resumed the isolate.
+ *
+ * \param paused Is the isolate paused on exit?
+ */
+DART_EXPORT void Dart_SetPausedOnExit(bool paused);
+
+/**
+ * Called when the embedder has caught a top level unhandled exception error
+ * in the current isolate.
+ *
+ * NOTE: It is illegal to call this twice on the same isolate without first
+ * clearing the sticky error to null.
+ *
+ * \param error The unhandled exception error.
+ */
+DART_EXPORT void Dart_SetStickyError(Dart_Handle error);
+
+/**
+ * Does the current isolate have a sticky error?
+ */
+DART_EXPORT bool Dart_HasStickyError(void);
+
+/**
+ * Gets the sticky error for the current isolate.
+ *
+ * \return A handle to the sticky error object or null.
+ */
+DART_EXPORT Dart_Handle Dart_GetStickyError(void);
+
+/**
+ * Handles the next pending message for the current isolate.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_HandleMessage(void);
+
+/**
+ * Drains the microtask queue, then blocks the calling thread until the current
+ * isolate receives a message, then handles all messages.
+ *
+ * \param timeout_millis When non-zero, the call returns after the indicated
+          number of milliseconds even if no message was received.
+ * \return A valid handle if no error occurs, otherwise an error handle.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_WaitForEvent(int64_t timeout_millis);
+
+/**
+ * Handles any pending messages for the vm service for the current
+ * isolate.
+ *
+ * This function may be used by an embedder at a breakpoint to avoid
+ * pausing the vm service.
+ *
+ * This function can indirectly cause the message notify callback to
+ * be called.
+ *
+ * \return true if the vm service requests the program resume
+ * execution, false otherwise
+ */
+DART_EXPORT bool Dart_HandleServiceMessages(void);
+
+/**
+ * Does the current isolate have pending service messages?
+ *
+ * \return true if the isolate has pending service messages, false otherwise.
+ */
+DART_EXPORT bool Dart_HasServiceMessages(void);
+
+/**
+ * Processes any incoming messages for the current isolate.
+ *
+ * This function may only be used when the embedder has not provided
+ * an alternate message delivery mechanism with
+ * Dart_SetMessageCallbacks. It is provided for convenience.
+ *
+ * This function waits for incoming messages for the current
+ * isolate. As new messages arrive, they are handled using
+ * Dart_HandleMessage. The routine exits when all ports to the
+ * current isolate are closed.
+ *
+ * \return A valid handle if the run loop exited successfully.  If an
+ *   exception or other error occurs while processing messages, an
+ *   error handle is returned.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_RunLoop(void);
+
+/**
+ * Lets the VM run message processing for the isolate.
+ *
+ * This function expects there to a current isolate and the current isolate
+ * must not have an active api scope. The VM will take care of making the
+ * isolate runnable (if not already), handles its message loop and will take
+ * care of shutting the isolate down once it's done.
+ *
+ * \param errors_are_fatal Whether uncaught errors should be fatal.
+ * \param on_error_port A port to notify on uncaught errors (or ILLEGAL_PORT).
+ * \param on_exit_port A port to notify on exit (or ILLEGAL_PORT).
+ * \param error A non-NULL pointer which will hold an error message if the call
+ *   fails. The error has to be free()ed by the caller.
+ *
+ * \return If successful the VM takes owernship of the isolate and takes care
+ *   of its message loop. If not successful the caller retains owernship of the
+ *   isolate.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT bool Dart_RunLoopAsync(
+    bool errors_are_fatal,
+    Dart_Port on_error_port,
+    Dart_Port on_exit_port,
+    char** error);
+
+/* TODO(turnidge): Should this be removed from the public api? */
+
+/**
+ * Gets the main port id for the current isolate.
+ */
+DART_EXPORT Dart_Port Dart_GetMainPortId(void);
+
+/**
+ * Does the current isolate have live ReceivePorts?
+ *
+ * A ReceivePort is live when it has not been closed.
+ */
+DART_EXPORT bool Dart_HasLivePorts(void);
+
+/**
+ * Posts a message for some isolate. The message is a serialized
+ * object.
+ *
+ * Requires there to be a current isolate.
+ *
+ * For posting messages outside of an isolate see \ref Dart_PostCObject.
+ *
+ * \param port_id The destination port.
+ * \param object An object from the current isolate.
+ *
+ * \return True if the message was posted.
+ */
+DART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle object);
+
+/**
+ * Returns a new SendPort with the provided port id.
+ *
+ * \param port_id The destination port.
+ *
+ * \return A new SendPort if no errors occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id);
+
+/**
+ * Gets the SendPort id for the provided SendPort.
+ * \param port A SendPort object whose id is desired.
+ * \param port_id Returns the id of the SendPort.
+ * \return Success if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port,
+                                           Dart_Port* port_id);
+
+/*
+ * ======
+ * Scopes
+ * ======
+ */
+
+/**
+ * Enters a new scope.
+ *
+ * All new local handles will be created in this scope. Additionally,
+ * some functions may return "scope allocated" memory which is only
+ * valid within this scope.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT void Dart_EnterScope(void);
+
+/**
+ * Exits a scope.
+ *
+ * The previous scope (if any) becomes the current scope.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT void Dart_ExitScope(void);
+
+/**
+ * The Dart VM uses "zone allocation" for temporary structures. Zones
+ * support very fast allocation of small chunks of memory. The chunks
+ * cannot be deallocated individually, but instead zones support
+ * deallocating all chunks in one fast operation.
+ *
+ * This function makes it possible for the embedder to allocate
+ * temporary data in the VMs zone allocator.
+ *
+ * Zone allocation is possible:
+ *   1. when inside a scope where local handles can be allocated
+ *   2. when processing a message from a native port in a native port
+ *      handler
+ *
+ * All the memory allocated this way will be reclaimed either on the
+ * next call to Dart_ExitScope or when the native port handler exits.
+ *
+ * \param size Size of the memory to allocate.
+ *
+ * \return A pointer to the allocated memory. NULL if allocation
+ *   failed. Failure might due to is no current VM zone.
+ */
+DART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size);
+
+/*
+ * =======
+ * Objects
+ * =======
+ */
+
+/**
+ * Returns the null object.
+ *
+ * \return A handle to the null object.
+ */
+DART_EXPORT Dart_Handle Dart_Null(void);
+
+/**
+ * Is this object null?
+ */
+DART_EXPORT bool Dart_IsNull(Dart_Handle object);
+
+/**
+ * Returns the empty string object.
+ *
+ * \return A handle to the empty string object.
+ */
+DART_EXPORT Dart_Handle Dart_EmptyString(void);
+
+/**
+ * Returns types that are not classes, and which therefore cannot be looked up
+ * as library members by Dart_GetType.
+ *
+ * \return A handle to the dynamic, void or Never type.
+ */
+DART_EXPORT Dart_Handle Dart_TypeDynamic(void);
+DART_EXPORT Dart_Handle Dart_TypeVoid(void);
+DART_EXPORT Dart_Handle Dart_TypeNever(void);
+
+/**
+ * Checks if the two objects are equal.
+ *
+ * The result of the comparison is returned through the 'equal'
+ * parameter. The return value itself is used to indicate success or
+ * failure, not equality.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param obj1 An object to be compared.
+ * \param obj2 An object to be compared.
+ * \param equal Returns the result of the equality comparison.
+ *
+ * \return A valid handle if no error occurs during the comparison.
+ */
+DART_EXPORT Dart_Handle Dart_ObjectEquals(Dart_Handle obj1,
+                                          Dart_Handle obj2,
+                                          bool* equal);
+
+/**
+ * Is this object an instance of some type?
+ *
+ * The result of the test is returned through the 'instanceof' parameter.
+ * The return value itself is used to indicate success or failure.
+ *
+ * \param object An object.
+ * \param type A type.
+ * \param instanceof Return true if 'object' is an instance of type 'type'.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_ObjectIsType(Dart_Handle object,
+                                          Dart_Handle type,
+                                          bool* instanceof);
+
+/**
+ * Query object type.
+ *
+ * \param object Some Object.
+ *
+ * \return true if Object is of the specified type.
+ */
+DART_EXPORT bool Dart_IsInstance(Dart_Handle object);
+DART_EXPORT bool Dart_IsNumber(Dart_Handle object);
+DART_EXPORT bool Dart_IsInteger(Dart_Handle object);
+DART_EXPORT bool Dart_IsDouble(Dart_Handle object);
+DART_EXPORT bool Dart_IsBoolean(Dart_Handle object);
+DART_EXPORT bool Dart_IsString(Dart_Handle object);
+DART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object); /* (ISO-8859-1) */
+DART_EXPORT bool Dart_IsExternalString(Dart_Handle object);
+DART_EXPORT bool Dart_IsList(Dart_Handle object);
+DART_EXPORT bool Dart_IsMap(Dart_Handle object);
+DART_EXPORT bool Dart_IsLibrary(Dart_Handle object);
+DART_EXPORT bool Dart_IsType(Dart_Handle handle);
+DART_EXPORT bool Dart_IsFunction(Dart_Handle handle);
+DART_EXPORT bool Dart_IsVariable(Dart_Handle handle);
+DART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle);
+DART_EXPORT bool Dart_IsClosure(Dart_Handle object);
+DART_EXPORT bool Dart_IsTypedData(Dart_Handle object);
+DART_EXPORT bool Dart_IsByteBuffer(Dart_Handle object);
+DART_EXPORT bool Dart_IsFuture(Dart_Handle object);
+
+/*
+ * =========
+ * Instances
+ * =========
+ */
+
+/*
+ * For the purposes of the embedding api, not all objects returned are
+ * Dart language objects.  Within the api, we use the term 'Instance'
+ * to indicate handles which refer to true Dart language objects.
+ *
+ * TODO(turnidge): Reorganize the "Object" section above, pulling down
+ * any functions that more properly belong here. */
+
+/**
+ * Gets the type of a Dart language object.
+ *
+ * \param instance Some Dart object.
+ *
+ * \return If no error occurs, the type is returned. Otherwise an
+ *   error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance);
+
+/**
+ * Returns the name for the provided class type.
+ *
+ * \return A valid string handle if no error occurs during the
+ *   operation.
+ */
+DART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type);
+
+/**
+ * Returns the name for the provided function or method.
+ *
+ * \return A valid string handle if no error occurs during the
+ *   operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function);
+
+/**
+ * Returns a handle to the owner of a function.
+ *
+ * The owner of an instance method or a static method is its defining
+ * class. The owner of a top-level function is its defining
+ * library. The owner of the function of a non-implicit closure is the
+ * function of the method or closure that defines the non-implicit
+ * closure.
+ *
+ * \return A valid handle to the owner of the function, or an error
+ *   handle if the argument is not a valid handle to a function.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function);
+
+/**
+ * Determines whether a function handle referes to a static function
+ * of method.
+ *
+ * For the purposes of the embedding API, a top-level function is
+ * implicitly declared static.
+ *
+ * \param function A handle to a function or method declaration.
+ * \param is_static Returns whether the function or method is declared static.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,
+                                              bool* is_static);
+
+/**
+ * Is this object a closure resulting from a tear-off (closurized method)?
+ *
+ * Returns true for closures produced when an ordinary method is accessed
+ * through a getter call. Returns false otherwise, in particular for closures
+ * produced from local function declarations.
+ *
+ * \param object Some Object.
+ *
+ * \return true if Object is a tear-off.
+ */
+DART_EXPORT bool Dart_IsTearOff(Dart_Handle object);
+
+/**
+ * Retrieves the function of a closure.
+ *
+ * \return A handle to the function of the closure, or an error handle if the
+ *   argument is not a closure.
+ */
+DART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure);
+
+/**
+ * Returns a handle to the library which contains class.
+ *
+ * \return A valid handle to the library with owns class, null if the class
+ *   has no library or an error handle if the argument is not a valid handle
+ *   to a class type.
+ */
+DART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type);
+
+/*
+ * =============================
+ * Numbers, Integers and Doubles
+ * =============================
+ */
+
+/**
+ * Does this Integer fit into a 64-bit signed integer?
+ *
+ * \param integer An integer.
+ * \param fits Returns true if the integer fits into a 64-bit signed integer.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer,
+                                                  bool* fits);
+
+/**
+ * Does this Integer fit into a 64-bit unsigned integer?
+ *
+ * \param integer An integer.
+ * \param fits Returns true if the integer fits into a 64-bit unsigned integer.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_IntegerFitsIntoUint64(Dart_Handle integer,
+                                                   bool* fits);
+
+/**
+ * Returns an Integer with the provided value.
+ *
+ * \param value The value of the integer.
+ *
+ * \return The Integer object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewInteger(int64_t value);
+
+/**
+ * Returns an Integer with the provided value.
+ *
+ * \param value The unsigned value of the integer.
+ *
+ * \return The Integer object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewIntegerFromUint64(uint64_t value);
+
+/**
+ * Returns an Integer with the provided value.
+ *
+ * \param value The value of the integer represented as a C string
+ *   containing a hexadecimal number.
+ *
+ * \return The Integer object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* value);
+
+/**
+ * Gets the value of an Integer.
+ *
+ * The integer must fit into a 64-bit signed integer, otherwise an error occurs.
+ *
+ * \param integer An Integer.
+ * \param value Returns the value of the Integer.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer,
+                                            int64_t* value);
+
+/**
+ * Gets the value of an Integer.
+ *
+ * The integer must fit into a 64-bit unsigned integer, otherwise an
+ * error occurs.
+ *
+ * \param integer An Integer.
+ * \param value Returns the value of the Integer.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_IntegerToUint64(Dart_Handle integer,
+                                             uint64_t* value);
+
+/**
+ * Gets the value of an integer as a hexadecimal C string.
+ *
+ * \param integer An Integer.
+ * \param value Returns the value of the Integer as a hexadecimal C
+ *   string. This C string is scope allocated and is only valid until
+ *   the next call to Dart_ExitScope.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer,
+                                                 const char** value);
+
+/**
+ * Returns a Double with the provided value.
+ *
+ * \param value A double.
+ *
+ * \return The Double object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewDouble(double value);
+
+/**
+ * Gets the value of a Double
+ *
+ * \param double_obj A Double
+ * \param value Returns the value of the Double.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value);
+
+/**
+ * Returns a closure of static function 'function_name' in the class 'class_name'
+ * in the exported namespace of specified 'library'.
+ *
+ * \param library Library object
+ * \param cls_type Type object representing a Class
+ * \param function_name Name of the static function in the class
+ *
+ * \return A valid Dart instance if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library,
+                                                    Dart_Handle cls_type,
+                                                    Dart_Handle function_name);
+
+/*
+ * ========
+ * Booleans
+ * ========
+ */
+
+/**
+ * Returns the True object.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \return A handle to the True object.
+ */
+DART_EXPORT Dart_Handle Dart_True(void);
+
+/**
+ * Returns the False object.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \return A handle to the False object.
+ */
+DART_EXPORT Dart_Handle Dart_False(void);
+
+/**
+ * Returns a Boolean with the provided value.
+ *
+ * \param value true or false.
+ *
+ * \return The Boolean object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewBoolean(bool value);
+
+/**
+ * Gets the value of a Boolean
+ *
+ * \param boolean_obj A Boolean
+ * \param value Returns the value of the Boolean.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, bool* value);
+
+/*
+ * =======
+ * Strings
+ * =======
+ */
+
+/**
+ * Gets the length of a String.
+ *
+ * \param str A String.
+ * \param length Returns the length of the String.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* length);
+
+/**
+ * Returns a String built from the provided C string
+ * (There is an implicit assumption that the C string passed in contains
+ *  UTF-8 encoded characters and '\0' is considered as a termination
+ *  character).
+ *
+ * \param str A C String
+ *
+ * \return The String object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str);
+/* TODO(turnidge): Document what happens when we run out of memory
+ * during this call. */
+
+/**
+ * Returns a String built from an array of UTF-8 encoded characters.
+ *
+ * \param utf8_array An array of UTF-8 encoded characters.
+ * \param length The length of the codepoints array.
+ *
+ * \return The String object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewStringFromUTF8(const uint8_t* utf8_array,
+                                               intptr_t length);
+
+/**
+ * Returns a String built from an array of UTF-16 encoded characters.
+ *
+ * \param utf16_array An array of UTF-16 encoded characters.
+ * \param length The length of the codepoints array.
+ *
+ * \return The String object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewStringFromUTF16(const uint16_t* utf16_array,
+                                                intptr_t length);
+
+/**
+ * Returns a String built from an array of UTF-32 encoded characters.
+ *
+ * \param utf32_array An array of UTF-32 encoded characters.
+ * \param length The length of the codepoints array.
+ *
+ * \return The String object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewStringFromUTF32(const int32_t* utf32_array,
+                                                intptr_t length);
+
+/**
+ * Returns a String which references an external array of
+ * Latin-1 (ISO-8859-1) encoded characters.
+ *
+ * \param latin1_array Array of Latin-1 encoded characters. This must not move.
+ * \param length The length of the characters array.
+ * \param peer An external pointer to associate with this string.
+ * \param external_allocation_size The number of externally allocated
+ *   bytes for peer. Used to inform the garbage collector.
+ * \param callback A callback to be called when this string is finalized.
+ *
+ * \return The String object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle
+Dart_NewExternalLatin1String(const uint8_t* latin1_array,
+                             intptr_t length,
+                             void* peer,
+                             intptr_t external_allocation_size,
+                             Dart_HandleFinalizer callback);
+
+/**
+ * Returns a String which references an external array of UTF-16 encoded
+ * characters.
+ *
+ * \param utf16_array An array of UTF-16 encoded characters. This must not move.
+ * \param length The length of the characters array.
+ * \param peer An external pointer to associate with this string.
+ * \param external_allocation_size The number of externally allocated
+ *   bytes for peer. Used to inform the garbage collector.
+ * \param callback A callback to be called when this string is finalized.
+ *
+ * \return The String object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle
+Dart_NewExternalUTF16String(const uint16_t* utf16_array,
+                            intptr_t length,
+                            void* peer,
+                            intptr_t external_allocation_size,
+                            Dart_HandleFinalizer callback);
+
+/**
+ * Gets the C string representation of a String.
+ * (It is a sequence of UTF-8 encoded values with a '\0' termination.)
+ *
+ * \param str A string.
+ * \param cstr Returns the String represented as a C string.
+ *   This C string is scope allocated and is only valid until
+ *   the next call to Dart_ExitScope.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle str,
+                                             const char** cstr);
+
+/**
+ * Gets a UTF-8 encoded representation of a String.
+ *
+ * Any unpaired surrogate code points in the string will be converted as
+ * replacement characters (U+FFFD, 0xEF 0xBF 0xBD in UTF-8). If you need
+ * to preserve unpaired surrogates, use the Dart_StringToUTF16 function.
+ *
+ * \param str A string.
+ * \param utf8_array Returns the String represented as UTF-8 code
+ *   units.  This UTF-8 array is scope allocated and is only valid
+ *   until the next call to Dart_ExitScope.
+ * \param length Used to return the length of the array which was
+ *   actually used.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str,
+                                          uint8_t** utf8_array,
+                                          intptr_t* length);
+
+/**
+ * Gets the data corresponding to the string object. This function returns
+ * the data only for Latin-1 (ISO-8859-1) string objects. For all other
+ * string objects it returns an error.
+ *
+ * \param str A string.
+ * \param latin1_array An array allocated by the caller, used to return
+ *   the string data.
+ * \param length Used to pass in the length of the provided array.
+ *   Used to return the length of the array which was actually used.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_StringToLatin1(Dart_Handle str,
+                                            uint8_t* latin1_array,
+                                            intptr_t* length);
+
+/**
+ * Gets the UTF-16 encoded representation of a string.
+ *
+ * \param str A string.
+ * \param utf16_array An array allocated by the caller, used to return
+ *   the array of UTF-16 encoded characters.
+ * \param length Used to pass in the length of the provided array.
+ *   Used to return the length of the array which was actually used.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_StringToUTF16(Dart_Handle str,
+                                           uint16_t* utf16_array,
+                                           intptr_t* length);
+
+/**
+ * Gets the storage size in bytes of a String.
+ *
+ * \param str A String.
+ * \param size Returns the storage size in bytes of the String.
+ *  This is the size in bytes needed to store the String.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_StringStorageSize(Dart_Handle str, intptr_t* size);
+
+/**
+ * Retrieves some properties associated with a String.
+ * Properties retrieved are:
+ * - character size of the string (one or two byte)
+ * - length of the string
+ * - peer pointer of string if it is an external string.
+ * \param str A String.
+ * \param char_size Returns the character size of the String.
+ * \param str_len Returns the length of the String.
+ * \param peer Returns the peer pointer associated with the String or 0 if
+ *   there is no peer pointer for it.
+ * \return Success if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_StringGetProperties(Dart_Handle str,
+                                                 intptr_t* char_size,
+                                                 intptr_t* str_len,
+                                                 void** peer);
+
+/*
+ * =====
+ * Lists
+ * =====
+ */
+
+/**
+ * Returns a List<dynamic> of the desired length.
+ *
+ * \param length The length of the list.
+ *
+ * \return The List object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewList(intptr_t length);
+
+typedef enum {
+  Dart_CoreType_Dynamic,
+  Dart_CoreType_Int,
+  Dart_CoreType_String,
+} Dart_CoreType_Id;
+
+// TODO(bkonyi): convert this to use nullable types once NNBD is enabled.
+/**
+ * Returns a List of the desired length with the desired legacy element type.
+ *
+ * \param element_type_id The type of elements of the list.
+ * \param length The length of the list.
+ *
+ * \return The List object if no error occurs. Otherwise returns an error
+ * handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewListOf(Dart_CoreType_Id element_type_id,
+                                       intptr_t length);
+
+/**
+ * Returns a List of the desired length with the desired element type.
+ *
+ * \param element_type Handle to a nullable type object. E.g., from
+ * Dart_GetType or Dart_GetNullableType.
+ *
+ * \param length The length of the list.
+ *
+ * \return The List object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewListOfType(Dart_Handle element_type,
+                                           intptr_t length);
+
+/**
+ * Returns a List of the desired length with the desired element type, filled
+ * with the provided object.
+ *
+ * \param element_type Handle to a type object. E.g., from Dart_GetType.
+ *
+ * \param fill_object Handle to an object of type 'element_type' that will be
+ * used to populate the list. This parameter can only be Dart_Null() if the
+ * length of the list is 0 or 'element_type' is a nullable type.
+ *
+ * \param length The length of the list.
+ *
+ * \return The List object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewListOfTypeFilled(Dart_Handle element_type,
+                                                 Dart_Handle fill_object,
+                                                 intptr_t length);
+
+/**
+ * Gets the length of a List.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param list A List.
+ * \param length Returns the length of the List.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_ListLength(Dart_Handle list, intptr_t* length);
+
+/**
+ * Gets the Object at some index of a List.
+ *
+ * If the index is out of bounds, an error occurs.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param list A List.
+ * \param index A valid index into the List.
+ *
+ * \return The Object in the List at the specified index if no error
+ *   occurs. Otherwise returns an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_ListGetAt(Dart_Handle list, intptr_t index);
+
+/**
+* Gets a range of Objects from a List.
+*
+* If any of the requested index values are out of bounds, an error occurs.
+*
+* May generate an unhandled exception error.
+*
+* \param list A List.
+* \param offset The offset of the first item to get.
+* \param length The number of items to get.
+* \param result A pointer to fill with the objects.
+*
+* \return Success if no error occurs during the operation.
+*/
+DART_EXPORT Dart_Handle Dart_ListGetRange(Dart_Handle list,
+                                          intptr_t offset,
+                                          intptr_t length,
+                                          Dart_Handle* result);
+
+/**
+ * Sets the Object at some index of a List.
+ *
+ * If the index is out of bounds, an error occurs.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param list A List.
+ * \param index A valid index into the List.
+ * \param value The Object to put in the List.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT Dart_Handle Dart_ListSetAt(Dart_Handle list,
+                                       intptr_t index,
+                                       Dart_Handle value);
+
+/**
+ * May generate an unhandled exception error.
+ */
+DART_EXPORT Dart_Handle Dart_ListGetAsBytes(Dart_Handle list,
+                                            intptr_t offset,
+                                            uint8_t* native_array,
+                                            intptr_t length);
+
+/**
+ * May generate an unhandled exception error.
+ */
+DART_EXPORT Dart_Handle Dart_ListSetAsBytes(Dart_Handle list,
+                                            intptr_t offset,
+                                            const uint8_t* native_array,
+                                            intptr_t length);
+
+/*
+ * ====
+ * Maps
+ * ====
+ */
+
+/**
+ * Gets the Object at some key of a Map.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param map A Map.
+ * \param key An Object.
+ *
+ * \return The value in the map at the specified key, null if the map does not
+ *   contain the key, or an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_MapGetAt(Dart_Handle map, Dart_Handle key);
+
+/**
+ * Returns whether the Map contains a given key.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param map A Map.
+ *
+ * \return A handle on a boolean indicating whether map contains the key.
+ *   Otherwise returns an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_MapContainsKey(Dart_Handle map, Dart_Handle key);
+
+/**
+ * Gets the list of keys of a Map.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param map A Map.
+ *
+ * \return The list of key Objects if no error occurs. Otherwise returns an
+ *   error handle.
+ */
+DART_EXPORT Dart_Handle Dart_MapKeys(Dart_Handle map);
+
+/*
+ * ==========
+ * Typed Data
+ * ==========
+ */
+
+typedef enum {
+  Dart_TypedData_kByteData = 0,
+  Dart_TypedData_kInt8,
+  Dart_TypedData_kUint8,
+  Dart_TypedData_kUint8Clamped,
+  Dart_TypedData_kInt16,
+  Dart_TypedData_kUint16,
+  Dart_TypedData_kInt32,
+  Dart_TypedData_kUint32,
+  Dart_TypedData_kInt64,
+  Dart_TypedData_kUint64,
+  Dart_TypedData_kFloat32,
+  Dart_TypedData_kFloat64,
+  Dart_TypedData_kInt32x4,
+  Dart_TypedData_kFloat32x4,
+  Dart_TypedData_kFloat64x2,
+  Dart_TypedData_kInvalid
+} Dart_TypedData_Type;
+
+/**
+ * Return type if this object is a TypedData object.
+ *
+ * \return kInvalid if the object is not a TypedData object or the appropriate
+ *   Dart_TypedData_Type.
+ */
+DART_EXPORT Dart_TypedData_Type Dart_GetTypeOfTypedData(Dart_Handle object);
+
+/**
+ * Return type if this object is an external TypedData object.
+ *
+ * \return kInvalid if the object is not an external TypedData object or
+ *   the appropriate Dart_TypedData_Type.
+ */
+DART_EXPORT Dart_TypedData_Type
+Dart_GetTypeOfExternalTypedData(Dart_Handle object);
+
+/**
+ * Returns a TypedData object of the desired length and type.
+ *
+ * \param type The type of the TypedData object.
+ * \param length The length of the TypedData object (length in type units).
+ *
+ * \return The TypedData object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewTypedData(Dart_TypedData_Type type,
+                                          intptr_t length);
+
+/**
+ * Returns a TypedData object which references an external data array.
+ *
+ * \param type The type of the data array.
+ * \param data A data array. This array must not move.
+ * \param length The length of the data array (length in type units).
+ *
+ * \return The TypedData object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewExternalTypedData(Dart_TypedData_Type type,
+                                                  void* data,
+                                                  intptr_t length);
+
+/**
+ * Returns a TypedData object which references an external data array.
+ *
+ * \param type The type of the data array.
+ * \param data A data array. This array must not move.
+ * \param length The length of the data array (length in type units).
+ * \param peer A pointer to a native object or NULL.  This value is
+ *   provided to callback when it is invoked.
+ * \param external_allocation_size The number of externally allocated
+ *   bytes for peer. Used to inform the garbage collector.
+ * \param callback A function pointer that will be invoked sometime
+ *   after the object is garbage collected, unless the handle has been deleted.
+ *   A valid callback needs to be specified it cannot be NULL.
+ *
+ * \return The TypedData object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle
+Dart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,
+                                       void* data,
+                                       intptr_t length,
+                                       void* peer,
+                                       intptr_t external_allocation_size,
+                                       Dart_HandleFinalizer callback);
+DART_EXPORT Dart_Handle Dart_NewUnmodifiableExternalTypedDataWithFinalizer(
+    Dart_TypedData_Type type,
+    const void* data,
+    intptr_t length,
+    void* peer,
+    intptr_t external_allocation_size,
+    Dart_HandleFinalizer callback);
+
+/**
+ * Returns a ByteBuffer object for the typed data.
+ *
+ * \param typed_data The TypedData object.
+ *
+ * \return The ByteBuffer object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewByteBuffer(Dart_Handle typed_data);
+
+/**
+ * Acquires access to the internal data address of a TypedData object.
+ *
+ * \param object The typed data object whose internal data address is to
+ *    be accessed.
+ * \param type The type of the object is returned here.
+ * \param data The internal data address is returned here.
+ * \param len Size of the typed array is returned here.
+ *
+ * Notes:
+ *   When the internal address of the object is acquired any calls to a
+ *   Dart API function that could potentially allocate an object or run
+ *   any Dart code will return an error.
+ *
+ *   Any Dart API functions for accessing the data should not be called
+ *   before the corresponding release. In particular, the object should
+ *   not be acquired again before its release. This leads to undefined
+ *   behavior.
+ *
+ * \return Success if the internal data address is acquired successfully.
+ *   Otherwise, returns an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_TypedDataAcquireData(Dart_Handle object,
+                                                  Dart_TypedData_Type* type,
+                                                  void** data,
+                                                  intptr_t* len);
+
+/**
+ * Releases access to the internal data address that was acquired earlier using
+ * Dart_TypedDataAcquireData.
+ *
+ * \param object The typed data object whose internal data address is to be
+ *   released.
+ *
+ * \return Success if the internal data address is released successfully.
+ *   Otherwise, returns an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle object);
+
+/**
+ * Returns the TypedData object associated with the ByteBuffer object.
+ *
+ * \param byte_buffer The ByteBuffer object.
+ *
+ * \return The TypedData object if no error occurs. Otherwise returns
+ *   an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_GetDataFromByteBuffer(Dart_Handle byte_buffer);
+
+/*
+ * ============================================================
+ * Invoking Constructors, Methods, Closures and Field accessors
+ * ============================================================
+ */
+
+/**
+ * Invokes a constructor, creating a new object.
+ *
+ * This function allows hidden constructors (constructors with leading
+ * underscores) to be called.
+ *
+ * \param type Type of object to be constructed.
+ * \param constructor_name The name of the constructor to invoke.  Use
+ *   Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor.
+ *   This name should not include the name of the class.
+ * \param number_of_arguments Size of the arguments array.
+ * \param arguments An array of arguments to the constructor.
+ *
+ * \return If the constructor is called and completes successfully,
+ *   then the new object. If an error occurs during execution, then an
+ *   error handle is returned.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_New(Dart_Handle type,
+         Dart_Handle constructor_name,
+         int number_of_arguments,
+         Dart_Handle* arguments);
+
+/**
+ * Allocate a new object without invoking a constructor.
+ *
+ * \param type The type of an object to be allocated.
+ *
+ * \return The new object. If an error occurs during execution, then an
+ *   error handle is returned.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_Allocate(Dart_Handle type);
+
+/**
+ * Allocate a new object without invoking a constructor, and sets specified
+ *  native fields.
+ *
+ * \param type The type of an object to be allocated.
+ * \param num_native_fields The number of native fields to set.
+ * \param native_fields An array containing the value of native fields.
+ *
+ * \return The new object. If an error occurs during execution, then an
+ *   error handle is returned.
+ */
+DART_EXPORT Dart_Handle
+Dart_AllocateWithNativeFields(Dart_Handle type,
+                              intptr_t num_native_fields,
+                              const intptr_t* native_fields);
+
+/**
+ * Invokes a method or function.
+ *
+ * The 'target' parameter may be an object, type, or library.  If
+ * 'target' is an object, then this function will invoke an instance
+ * method.  If 'target' is a type, then this function will invoke a
+ * static method.  If 'target' is a library, then this function will
+ * invoke a top-level function from that library.
+ * NOTE: This API call cannot be used to invoke methods of a type object.
+ *
+ * This function ignores visibility (leading underscores in names).
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param target An object, type, or library.
+ * \param name The name of the function or method to invoke.
+ * \param number_of_arguments Size of the arguments array.
+ * \param arguments An array of arguments to the function.
+ *
+ * \return If the function or method is called and completes
+ *   successfully, then the return value is returned. If an error
+ *   occurs during execution, then an error handle is returned.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_Invoke(Dart_Handle target,
+            Dart_Handle name,
+            int number_of_arguments,
+            Dart_Handle* arguments);
+/* TODO(turnidge): Document how to invoke operators. */
+
+/**
+ * Invokes a Closure with the given arguments.
+ *
+ * May generate an unhandled exception error.
+ *
+ * \return If no error occurs during execution, then the result of
+ *   invoking the closure is returned. If an error occurs during
+ *   execution, then an error handle is returned.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_InvokeClosure(Dart_Handle closure,
+                   int number_of_arguments,
+                   Dart_Handle* arguments);
+
+/**
+ * Invokes a Generative Constructor on an object that was previously
+ * allocated using Dart_Allocate/Dart_AllocateWithNativeFields.
+ *
+ * The 'object' parameter must be an object.
+ *
+ * This function ignores visibility (leading underscores in names).
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param object An object.
+ * \param name The name of the constructor to invoke.
+ *   Use Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor.
+ * \param number_of_arguments Size of the arguments array.
+ * \param arguments An array of arguments to the function.
+ *
+ * \return If the constructor is called and completes
+ *   successfully, then the object is returned. If an error
+ *   occurs during execution, then an error handle is returned.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_InvokeConstructor(Dart_Handle object,
+                       Dart_Handle name,
+                       int number_of_arguments,
+                       Dart_Handle* arguments);
+
+/**
+ * Gets the value of a field.
+ *
+ * The 'container' parameter may be an object, type, or library.  If
+ * 'container' is an object, then this function will access an
+ * instance field.  If 'container' is a type, then this function will
+ * access a static field.  If 'container' is a library, then this
+ * function will access a top-level variable.
+ * NOTE: This API call cannot be used to access fields of a type object.
+ *
+ * This function ignores field visibility (leading underscores in names).
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param container An object, type, or library.
+ * \param name A field name.
+ *
+ * \return If no error occurs, then the value of the field is
+ *   returned. Otherwise an error handle is returned.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_GetField(Dart_Handle container, Dart_Handle name);
+
+/**
+ * Sets the value of a field.
+ *
+ * The 'container' parameter may actually be an object, type, or
+ * library.  If 'container' is an object, then this function will
+ * access an instance field.  If 'container' is a type, then this
+ * function will access a static field.  If 'container' is a library,
+ * then this function will access a top-level variable.
+ * NOTE: This API call cannot be used to access fields of a type object.
+ *
+ * This function ignores field visibility (leading underscores in names).
+ *
+ * May generate an unhandled exception error.
+ *
+ * \param container An object, type, or library.
+ * \param name A field name.
+ * \param value The new field value.
+ *
+ * \return A valid handle if no error occurs.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_SetField(Dart_Handle container, Dart_Handle name, Dart_Handle value);
+
+/*
+ * ==========
+ * Exceptions
+ * ==========
+ */
+
+/*
+ * TODO(turnidge): Remove these functions from the api and replace all
+ * uses with Dart_NewUnhandledExceptionError. */
+
+/**
+ * Throws an exception.
+ *
+ * This function causes a Dart language exception to be thrown. This
+ * will proceed in the standard way, walking up Dart frames until an
+ * appropriate 'catch' block is found, executing 'finally' blocks,
+ * etc.
+ *
+ * If an error handle is passed into this function, the error is
+ * propagated immediately.  See Dart_PropagateError for a discussion
+ * of error propagation.
+ *
+ * If successful, this function does not return. Note that this means
+ * that the destructors of any stack-allocated C++ objects will not be
+ * called. If there are no Dart frames on the stack, an error occurs.
+ *
+ * \return An error handle if the exception was not thrown.
+ *   Otherwise the function does not return.
+ */
+DART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception);
+
+/**
+ * Rethrows an exception.
+ *
+ * Rethrows an exception, unwinding all dart frames on the stack. If
+ * successful, this function does not return. Note that this means
+ * that the destructors of any stack-allocated C++ objects will not be
+ * called. If there are no Dart frames on the stack, an error occurs.
+ *
+ * \return An error handle if the exception was not thrown.
+ *   Otherwise the function does not return.
+ */
+DART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception,
+                                              Dart_Handle stacktrace);
+
+/*
+ * ===========================
+ * Native fields and functions
+ * ===========================
+ */
+
+/**
+ * Gets the number of native instance fields in an object.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj,
+                                                         int* count);
+
+/**
+ * Gets the value of a native field.
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj,
+                                                    int index,
+                                                    intptr_t* value);
+
+/**
+ * Sets the value of a native field.
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj,
+                                                    int index,
+                                                    intptr_t value);
+
+/**
+ * The arguments to a native function.
+ *
+ * This object is passed to a native function to represent its
+ * arguments and return value. It allows access to the arguments to a
+ * native function by index. It also allows the return value of a
+ * native function to be set.
+ */
+typedef struct _Dart_NativeArguments* Dart_NativeArguments;
+
+/**
+ * Extracts current isolate group data from the native arguments structure.
+ */
+DART_EXPORT void* Dart_GetNativeIsolateGroupData(Dart_NativeArguments args);
+
+typedef enum {
+  Dart_NativeArgument_kBool = 0,
+  Dart_NativeArgument_kInt32,
+  Dart_NativeArgument_kUint32,
+  Dart_NativeArgument_kInt64,
+  Dart_NativeArgument_kUint64,
+  Dart_NativeArgument_kDouble,
+  Dart_NativeArgument_kString,
+  Dart_NativeArgument_kInstance,
+  Dart_NativeArgument_kNativeFields,
+} Dart_NativeArgument_Type;
+
+typedef struct _Dart_NativeArgument_Descriptor {
+  uint8_t type;
+  uint8_t index;
+} Dart_NativeArgument_Descriptor;
+
+typedef union _Dart_NativeArgument_Value {
+  bool as_bool;
+  int32_t as_int32;
+  uint32_t as_uint32;
+  int64_t as_int64;
+  uint64_t as_uint64;
+  double as_double;
+  struct {
+    Dart_Handle dart_str;
+    void* peer;
+  } as_string;
+  struct {
+    intptr_t num_fields;
+    intptr_t* values;
+  } as_native_fields;
+  Dart_Handle as_instance;
+} Dart_NativeArgument_Value;
+
+enum {
+  kNativeArgNumberPos = 0,
+  kNativeArgNumberSize = 8,
+  kNativeArgTypePos = kNativeArgNumberPos + kNativeArgNumberSize,
+  kNativeArgTypeSize = 8,
+};
+
+#define BITMASK(size) ((1 << size) - 1)
+#define DART_NATIVE_ARG_DESCRIPTOR(type, position)                             \
+  (((type & BITMASK(kNativeArgTypeSize)) << kNativeArgTypePos) |               \
+   (position & BITMASK(kNativeArgNumberSize)))
+
+/**
+ * Gets the native arguments based on the types passed in and populates
+ * the passed arguments buffer with appropriate native values.
+ *
+ * \param args the Native arguments block passed into the native call.
+ * \param num_arguments length of argument descriptor array and argument
+ *   values array passed in.
+ * \param arg_descriptors an array that describes the arguments that
+ *   need to be retrieved. For each argument to be retrieved the descriptor
+ *   contains the argument number (0, 1 etc.) and the argument type
+ *   described using Dart_NativeArgument_Type, e.g:
+ *   DART_NATIVE_ARG_DESCRIPTOR(Dart_NativeArgument_kBool, 1) indicates
+ *   that the first argument is to be retrieved and it should be a boolean.
+ * \param arg_values array into which the native arguments need to be
+ *   extracted into, the array is allocated by the caller (it could be
+ *   stack allocated to avoid the malloc/free performance overhead).
+ *
+ * \return Success if all the arguments could be extracted correctly,
+ *   returns an error handle if there were any errors while extracting the
+ *   arguments (mismatched number of arguments, incorrect types, etc.).
+ */
+DART_EXPORT Dart_Handle
+Dart_GetNativeArguments(Dart_NativeArguments args,
+                        int num_arguments,
+                        const Dart_NativeArgument_Descriptor* arg_descriptors,
+                        Dart_NativeArgument_Value* arg_values);
+
+/**
+ * Gets the native argument at some index.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args,
+                                               int index);
+/* TODO(turnidge): Specify the behavior of an out-of-bounds access. */
+
+/**
+ * Gets the number of native arguments.
+ */
+DART_EXPORT int Dart_GetNativeArgumentCount(Dart_NativeArguments args);
+
+/**
+ * Gets all the native fields of the native argument at some index.
+ * \param args Native arguments structure.
+ * \param arg_index Index of the desired argument in the structure above.
+ * \param num_fields size of the intptr_t array 'field_values' passed in.
+ * \param field_values intptr_t array in which native field values are returned.
+ * \return Success if the native fields where copied in successfully. Otherwise
+ *   returns an error handle. On success the native field values are copied
+ *   into the 'field_values' array, if the argument at 'arg_index' is a
+ *   null object then 0 is copied as the native field values into the
+ *   'field_values' array.
+ */
+DART_EXPORT Dart_Handle
+Dart_GetNativeFieldsOfArgument(Dart_NativeArguments args,
+                               int arg_index,
+                               int num_fields,
+                               intptr_t* field_values);
+
+/**
+ * Gets the native field of the receiver.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeReceiver(Dart_NativeArguments args,
+                                               intptr_t* value);
+
+/**
+ * Gets a string native argument at some index.
+ * \param args Native arguments structure.
+ * \param arg_index Index of the desired argument in the structure above.
+ * \param peer Returns the peer pointer if the string argument has one.
+ * \return Success if the string argument has a peer, if it does not
+ *   have a peer then the String object is returned. Otherwise returns
+ *   an error handle (argument is not a String object).
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeStringArgument(Dart_NativeArguments args,
+                                                     int arg_index,
+                                                     void** peer);
+
+/**
+ * Gets an integer native argument at some index.
+ * \param args Native arguments structure.
+ * \param index Index of the desired argument in the structure above.
+ * \param value Returns the integer value if the argument is an Integer.
+ * \return Success if no error occurs. Otherwise returns an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeIntegerArgument(Dart_NativeArguments args,
+                                                      int index,
+                                                      int64_t* value);
+
+/**
+ * Gets a boolean native argument at some index.
+ * \param args Native arguments structure.
+ * \param index Index of the desired argument in the structure above.
+ * \param value Returns the boolean value if the argument is a Boolean.
+ * \return Success if no error occurs. Otherwise returns an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeBooleanArgument(Dart_NativeArguments args,
+                                                      int index,
+                                                      bool* value);
+
+/**
+ * Gets a double native argument at some index.
+ * \param args Native arguments structure.
+ * \param index Index of the desired argument in the structure above.
+ * \param value Returns the double value if the argument is a double.
+ * \return Success if no error occurs. Otherwise returns an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeDoubleArgument(Dart_NativeArguments args,
+                                                     int index,
+                                                     double* value);
+
+/**
+ * Sets the return value for a native function.
+ *
+ * If retval is an Error handle, then error will be propagated once
+ * the native functions exits. See Dart_PropagateError for a
+ * discussion of how different types of errors are propagated.
+ */
+DART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args,
+                                     Dart_Handle retval);
+
+DART_EXPORT void Dart_SetWeakHandleReturnValue(Dart_NativeArguments args,
+                                               Dart_WeakPersistentHandle rval);
+
+DART_EXPORT void Dart_SetBooleanReturnValue(Dart_NativeArguments args,
+                                            bool retval);
+
+DART_EXPORT void Dart_SetIntegerReturnValue(Dart_NativeArguments args,
+                                            int64_t retval);
+
+DART_EXPORT void Dart_SetDoubleReturnValue(Dart_NativeArguments args,
+                                           double retval);
+
+/**
+ * A native function.
+ */
+typedef void (*Dart_NativeFunction)(Dart_NativeArguments arguments);
+
+/**
+ * Native entry resolution callback.
+ *
+ * For libraries and scripts which have native functions, the embedder
+ * can provide a native entry resolver. This callback is used to map a
+ * name/arity to a Dart_NativeFunction. If no function is found, the
+ * callback should return NULL.
+ *
+ * The parameters to the native resolver function are:
+ * \param name a Dart string which is the name of the native function.
+ * \param num_of_arguments is the number of arguments expected by the
+ *   native function.
+ * \param auto_setup_scope is a boolean flag that can be set by the resolver
+ *   to indicate if this function needs a Dart API scope (see Dart_EnterScope/
+ *   Dart_ExitScope) to be setup automatically by the VM before calling into
+ *   the native function. By default most native functions would require this
+ *   to be true but some light weight native functions which do not call back
+ *   into the VM through the Dart API may not require a Dart scope to be
+ *   setup automatically.
+ *
+ * \return A valid Dart_NativeFunction which resolves to a native entry point
+ *   for the native function.
+ *
+ * See Dart_SetNativeResolver.
+ */
+typedef Dart_NativeFunction (*Dart_NativeEntryResolver)(Dart_Handle name,
+                                                        int num_of_arguments,
+                                                        bool* auto_setup_scope);
+/* TODO(turnidge): Consider renaming to NativeFunctionResolver or
+ * NativeResolver. */
+
+/**
+ * Native entry symbol lookup callback.
+ *
+ * For libraries and scripts which have native functions, the embedder
+ * can provide a callback for mapping a native entry to a symbol. This callback
+ * maps a native function entry PC to the native function name. If no native
+ * entry symbol can be found, the callback should return NULL.
+ *
+ * The parameters to the native reverse resolver function are:
+ * \param nf A Dart_NativeFunction.
+ *
+ * \return A const UTF-8 string containing the symbol name or NULL.
+ *
+ * See Dart_SetNativeResolver.
+ */
+typedef const uint8_t* (*Dart_NativeEntrySymbol)(Dart_NativeFunction nf);
+
+/**
+ * FFI Native C function pointer resolver callback.
+ *
+ * See Dart_SetFfiNativeResolver.
+ */
+typedef void* (*Dart_FfiNativeResolver)(const char* name, uintptr_t args_n);
+
+/*
+ * ===========
+ * Environment
+ * ===========
+ */
+
+/**
+ * An environment lookup callback function.
+ *
+ * \param name The name of the value to lookup in the environment.
+ *
+ * \return A valid handle to a string if the name exists in the
+ * current environment or Dart_Null() if not.
+ */
+typedef Dart_Handle (*Dart_EnvironmentCallback)(Dart_Handle name);
+
+/**
+ * Sets the environment callback for the current isolate. This
+ * callback is used to lookup environment values by name in the
+ * current environment. This enables the embedder to supply values for
+ * the const constructors bool.fromEnvironment, int.fromEnvironment
+ * and String.fromEnvironment.
+ */
+DART_EXPORT Dart_Handle
+Dart_SetEnvironmentCallback(Dart_EnvironmentCallback callback);
+
+/**
+ * Sets the callback used to resolve native functions for a library.
+ *
+ * \param library A library.
+ * \param resolver A native entry resolver.
+ *
+ * \return A valid handle if the native resolver was set successfully.
+ */
+DART_EXPORT Dart_Handle
+Dart_SetNativeResolver(Dart_Handle library,
+                       Dart_NativeEntryResolver resolver,
+                       Dart_NativeEntrySymbol symbol);
+/* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */
+
+/**
+ * Returns the callback used to resolve native functions for a library.
+ *
+ * \param library A library.
+ * \param resolver a pointer to a Dart_NativeEntryResolver
+ *
+ * \return A valid handle if the library was found.
+ */
+DART_EXPORT Dart_Handle
+Dart_GetNativeResolver(Dart_Handle library, Dart_NativeEntryResolver* resolver);
+
+/**
+ * Returns the callback used to resolve native function symbols for a library.
+ *
+ * \param library A library.
+ * \param resolver a pointer to a Dart_NativeEntrySymbol.
+ *
+ * \return A valid handle if the library was found.
+ */
+DART_EXPORT Dart_Handle Dart_GetNativeSymbol(Dart_Handle library,
+                                             Dart_NativeEntrySymbol* resolver);
+
+/**
+ * Sets the callback used to resolve FFI native functions for a library.
+ * The resolved functions are expected to be a C function pointer of the
+ * correct signature (as specified in the `@FfiNative<NFT>()` function
+ * annotation in Dart code).
+ *
+ * NOTE: This is an experimental feature and might change in the future.
+ *
+ * \param library A library.
+ * \param resolver A native function resolver.
+ *
+ * \return A valid handle if the native resolver was set successfully.
+ */
+DART_EXPORT Dart_Handle
+Dart_SetFfiNativeResolver(Dart_Handle library, Dart_FfiNativeResolver resolver);
+
+/*
+ * =====================
+ * Scripts and Libraries
+ * =====================
+ */
+
+typedef enum {
+  Dart_kCanonicalizeUrl = 0,
+  Dart_kImportTag,
+  Dart_kKernelTag,
+} Dart_LibraryTag;
+
+/**
+ * The library tag handler is a multi-purpose callback provided by the
+ * embedder to the Dart VM. The embedder implements the tag handler to
+ * provide the ability to load Dart scripts and imports.
+ *
+ * -- TAGS --
+ *
+ * Dart_kCanonicalizeUrl
+ *
+ * This tag indicates that the embedder should canonicalize 'url' with
+ * respect to 'library'.  For most embedders, the
+ * Dart_DefaultCanonicalizeUrl function is a sufficient implementation
+ * of this tag.  The return value should be a string holding the
+ * canonicalized url.
+ *
+ * Dart_kImportTag
+ *
+ * This tag is used to load a library from IsolateMirror.loadUri. The embedder
+ * should call Dart_LoadLibraryFromKernel to provide the library to the VM. The
+ * return value should be an error or library (the result from
+ * Dart_LoadLibraryFromKernel).
+ *
+ * Dart_kKernelTag
+ *
+ * This tag is used to load the intermediate file (kernel) generated by
+ * the Dart front end. This tag is typically used when a 'hot-reload'
+ * of an application is needed and the VM is 'use dart front end' mode.
+ * The dart front end typically compiles all the scripts, imports and part
+ * files into one intermediate file hence we don't use the source/import or
+ * script tags. The return value should be an error or a TypedData containing
+ * the kernel bytes.
+ *
+ */
+typedef Dart_Handle (*Dart_LibraryTagHandler)(
+    Dart_LibraryTag tag,
+    Dart_Handle library_or_package_map_url,
+    Dart_Handle url);
+
+/**
+ * Sets library tag handler for the current isolate. This handler is
+ * used to handle the various tags encountered while loading libraries
+ * or scripts in the isolate.
+ *
+ * \param handler Handler code to be used for handling the various tags
+ *   encountered while loading libraries or scripts in the isolate.
+ *
+ * \return If no error occurs, the handler is set for the isolate.
+ *   Otherwise an error handle is returned.
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT Dart_Handle
+Dart_SetLibraryTagHandler(Dart_LibraryTagHandler handler);
+
+/**
+ * Handles deferred loading requests. When this handler is invoked, it should
+ * eventually load the deferred loading unit with the given id and call
+ * Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError. It is
+ * recommended that the loading occur asynchronously, but it is permitted to
+ * call Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError before the
+ * handler returns.
+ *
+ * If an error is returned, it will be propagated through
+ * `prefix.loadLibrary()`. This is useful for synchronous
+ * implementations, which must propagate any unwind errors from
+ * Dart_DeferredLoadComplete or Dart_DeferredLoadComplete. Otherwise the handler
+ * should return a non-error such as `Dart_Null()`.
+ */
+typedef Dart_Handle (*Dart_DeferredLoadHandler)(intptr_t loading_unit_id);
+
+/**
+ * Sets the deferred load handler for the current isolate. This handler is
+ * used to handle loading deferred imports in an AppJIT or AppAOT program.
+ */
+DART_EXPORT Dart_Handle
+Dart_SetDeferredLoadHandler(Dart_DeferredLoadHandler handler);
+
+/**
+ * Notifies the VM that a deferred load completed successfully. This function
+ * will eventually cause the corresponding `prefix.loadLibrary()` futures to
+ * complete.
+ *
+ * Requires the current isolate to be the same current isolate during the
+ * invocation of the Dart_DeferredLoadHandler.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_DeferredLoadComplete(intptr_t loading_unit_id,
+                          const uint8_t* snapshot_data,
+                          const uint8_t* snapshot_instructions);
+
+/**
+ * Notifies the VM that a deferred load failed. This function
+ * will eventually cause the corresponding `prefix.loadLibrary()` futures to
+ * complete with an error.
+ *
+ * If `transient` is true, future invocations of `prefix.loadLibrary()` will
+ * trigger new load requests. If false, futures invocation will complete with
+ * the same error.
+ *
+ * Requires the current isolate to be the same current isolate during the
+ * invocation of the Dart_DeferredLoadHandler.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_DeferredLoadCompleteError(intptr_t loading_unit_id,
+                               const char* error_message,
+                               bool transient);
+
+/**
+ * Canonicalizes a url with respect to some library.
+ *
+ * The url is resolved with respect to the library's url and some url
+ * normalizations are performed.
+ *
+ * This canonicalization function should be sufficient for most
+ * embedders to implement the Dart_kCanonicalizeUrl tag.
+ *
+ * \param base_url The base url relative to which the url is
+ *                being resolved.
+ * \param url The url being resolved and canonicalized.  This
+ *            parameter is a string handle.
+ *
+ * \return If no error occurs, a String object is returned.  Otherwise
+ *   an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_DefaultCanonicalizeUrl(Dart_Handle base_url,
+                                                    Dart_Handle url);
+
+/**
+ * Loads the root library for the current isolate.
+ *
+ * Requires there to be no current root library.
+ *
+ * \param kernel_buffer A buffer which contains a kernel binary (see
+ *     pkg/kernel/binary.md). Must remain valid until isolate group shutdown.
+ * \param kernel_size Length of the passed in buffer.
+ *
+ * \return A handle to the root library, or an error.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_LoadScriptFromKernel(const uint8_t* kernel_buffer, intptr_t kernel_size);
+
+/**
+ * Gets the library for the root script for the current isolate.
+ *
+ * If the root script has not yet been set for the current isolate,
+ * this function returns Dart_Null().  This function never returns an
+ * error handle.
+ *
+ * \return Returns the root Library for the current isolate or Dart_Null().
+ */
+DART_EXPORT Dart_Handle Dart_RootLibrary(void);
+
+/**
+ * Sets the root library for the current isolate.
+ *
+ * \return Returns an error handle if `library` is not a library handle.
+ */
+DART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library);
+
+/**
+ * Lookup or instantiate a legacy type by name and type arguments from a
+ * Library.
+ *
+ * \param library The library containing the class or interface.
+ * \param class_name The class name for the type.
+ * \param number_of_type_arguments Number of type arguments.
+ *   For non parametric types the number of type arguments would be 0.
+ * \param type_arguments Pointer to an array of type arguments.
+ *   For non parameteric types a NULL would be passed in for this argument.
+ *
+ * \return If no error occurs, the type is returned.
+ *   Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,
+                                     Dart_Handle class_name,
+                                     intptr_t number_of_type_arguments,
+                                     Dart_Handle* type_arguments);
+
+/**
+ * Lookup or instantiate a nullable type by name and type arguments from
+ * Library.
+ *
+ * \param library The library containing the class or interface.
+ * \param class_name The class name for the type.
+ * \param number_of_type_arguments Number of type arguments.
+ *   For non parametric types the number of type arguments would be 0.
+ * \param type_arguments Pointer to an array of type arguments.
+ *   For non parameteric types a NULL would be passed in for this argument.
+ *
+ * \return If no error occurs, the type is returned.
+ *   Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_GetNullableType(Dart_Handle library,
+                                             Dart_Handle class_name,
+                                             intptr_t number_of_type_arguments,
+                                             Dart_Handle* type_arguments);
+
+/**
+ * Lookup or instantiate a non-nullable type by name and type arguments from
+ * Library.
+ *
+ * \param library The library containing the class or interface.
+ * \param class_name The class name for the type.
+ * \param number_of_type_arguments Number of type arguments.
+ *   For non parametric types the number of type arguments would be 0.
+ * \param type_arguments Pointer to an array of type arguments.
+ *   For non parameteric types a NULL would be passed in for this argument.
+ *
+ * \return If no error occurs, the type is returned.
+ *   Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle
+Dart_GetNonNullableType(Dart_Handle library,
+                        Dart_Handle class_name,
+                        intptr_t number_of_type_arguments,
+                        Dart_Handle* type_arguments);
+
+/**
+ * Creates a nullable version of the provided type.
+ *
+ * \param type The type to be converted to a nullable type.
+ *
+ * \return If no error occurs, a nullable type is returned.
+ *   Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_TypeToNullableType(Dart_Handle type);
+
+/**
+ * Creates a non-nullable version of the provided type.
+ *
+ * \param type The type to be converted to a non-nullable type.
+ *
+ * \return If no error occurs, a non-nullable type is returned.
+ *   Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_TypeToNonNullableType(Dart_Handle type);
+
+/**
+ * A type's nullability.
+ *
+ * \param type A Dart type.
+ * \param result An out parameter containing the result of the check. True if
+ * the type is of the specified nullability, false otherwise.
+ *
+ * \return Returns an error handle if type is not of type Type.
+ */
+DART_EXPORT Dart_Handle Dart_IsNullableType(Dart_Handle type, bool* result);
+DART_EXPORT Dart_Handle Dart_IsNonNullableType(Dart_Handle type, bool* result);
+DART_EXPORT Dart_Handle Dart_IsLegacyType(Dart_Handle type, bool* result);
+
+/**
+ * Lookup a class or interface by name from a Library.
+ *
+ * \param library The library containing the class or interface.
+ * \param class_name The name of the class or interface.
+ *
+ * \return If no error occurs, the class or interface is
+ *   returned. Otherwise an error handle is returned.
+ */
+DART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library,
+                                      Dart_Handle class_name);
+/* TODO(asiva): The above method needs to be removed once all uses
+ * of it are removed from the embedder code. */
+
+/**
+ * Returns an import path to a Library, such as "file:///test.dart" or
+ * "dart:core".
+ */
+DART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library);
+
+/**
+ * Returns a URL from which a Library was loaded.
+ */
+DART_EXPORT Dart_Handle Dart_LibraryResolvedUrl(Dart_Handle library);
+
+/**
+ * \return An array of libraries.
+ */
+DART_EXPORT Dart_Handle Dart_GetLoadedLibraries(void);
+
+DART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url);
+/* TODO(turnidge): Consider returning Dart_Null() when the library is
+ * not found to distinguish that from a true error case. */
+
+/**
+ * Report an loading error for the library.
+ *
+ * \param library The library that failed to load.
+ * \param error The Dart error instance containing the load error.
+ *
+ * \return If the VM handles the error, the return value is
+ * a null handle. If it doesn't handle the error, the error
+ * object is returned.
+ */
+DART_EXPORT Dart_Handle Dart_LibraryHandleError(Dart_Handle library,
+                                                Dart_Handle error);
+
+/**
+ * Called by the embedder to load a partial program. Does not set the root
+ * library.
+ *
+ * \param kernel_buffer A buffer which contains a kernel binary (see
+ *     pkg/kernel/binary.md). Must remain valid until isolate shutdown.
+ * \param kernel_buffer_size Length of the passed in buffer.
+ *
+ * \return A handle to the main library of the compilation unit, or an error.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_LoadLibraryFromKernel(const uint8_t* kernel_buffer,
+                           intptr_t kernel_buffer_size);
+
+/**
+ * Indicates that all outstanding load requests have been satisfied.
+ * This finalizes all the new classes loaded and optionally completes
+ * deferred library futures.
+ *
+ * Requires there to be a current isolate.
+ *
+ * \param complete_futures Specify true if all deferred library
+ *  futures should be completed, false otherwise.
+ *
+ * \return Success if all classes have been finalized and deferred library
+ *   futures are completed. Otherwise, returns an error.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_FinalizeLoading(bool complete_futures);
+
+/*
+ * =====
+ * Peers
+ * =====
+ */
+
+/**
+ * The peer field is a lazily allocated field intended for storage of
+ * an uncommonly used values.  Most instances types can have a peer
+ * field allocated.  The exceptions are subtypes of Null, num, and
+ * bool.
+ */
+
+/**
+ * Returns the value of peer field of 'object' in 'peer'.
+ *
+ * \param object An object.
+ * \param peer An out parameter that returns the value of the peer
+ *   field.
+ *
+ * \return Returns an error if 'object' is a subtype of Null, num, or
+ *   bool.
+ */
+DART_EXPORT Dart_Handle Dart_GetPeer(Dart_Handle object, void** peer);
+
+/**
+ * Sets the value of the peer field of 'object' to the value of
+ * 'peer'.
+ *
+ * \param object An object.
+ * \param peer A value to store in the peer field.
+ *
+ * \return Returns an error if 'object' is a subtype of Null, num, or
+ *   bool.
+ */
+DART_EXPORT Dart_Handle Dart_SetPeer(Dart_Handle object, void* peer);
+
+/*
+ * ======
+ * Kernel
+ * ======
+ */
+
+/**
+ * Experimental support for Dart to Kernel parser isolate.
+ *
+ * TODO(hausner): Document finalized interface.
+ *
+ */
+
+// TODO(33433): Remove kernel service from the embedding API.
+
+typedef enum {
+  Dart_KernelCompilationStatus_Unknown = -1,
+  Dart_KernelCompilationStatus_Ok = 0,
+  Dart_KernelCompilationStatus_Error = 1,
+  Dart_KernelCompilationStatus_Crash = 2,
+  Dart_KernelCompilationStatus_MsgFailed = 3,
+} Dart_KernelCompilationStatus;
+
+typedef struct {
+  Dart_KernelCompilationStatus status;
+  bool null_safety;
+  char* error;
+  uint8_t* kernel;
+  intptr_t kernel_size;
+} Dart_KernelCompilationResult;
+
+typedef enum {
+  Dart_KernelCompilationVerbosityLevel_Error = 0,
+  Dart_KernelCompilationVerbosityLevel_Warning,
+  Dart_KernelCompilationVerbosityLevel_Info,
+  Dart_KernelCompilationVerbosityLevel_All,
+} Dart_KernelCompilationVerbosityLevel;
+
+DART_EXPORT bool Dart_IsKernelIsolate(Dart_Isolate isolate);
+DART_EXPORT bool Dart_KernelIsolateIsRunning(void);
+DART_EXPORT Dart_Port Dart_KernelPort(void);
+
+/**
+ * Compiles the given `script_uri` to a kernel file.
+ *
+ * \param platform_kernel A buffer containing the kernel of the platform (e.g.
+ * `vm_platform_strong.dill`). The VM does not take ownership of this memory.
+ *
+ * \param platform_kernel_size The length of the platform_kernel buffer.
+ *
+ * \param snapshot_compile Set to `true` when the compilation is for a snapshot.
+ * This is used by the frontend to determine if compilation related information
+ * should be printed to console (e.g., null safety mode).
+ *
+ * \param verbosity Specifies the logging behavior of the kernel compilation
+ * service.
+ *
+ * \return Returns the result of the compilation.
+ *
+ * On a successful compilation the returned [Dart_KernelCompilationResult] has
+ * a status of [Dart_KernelCompilationStatus_Ok] and the `kernel`/`kernel_size`
+ * fields are set. The caller takes ownership of the malloc()ed buffer.
+ *
+ * On a failed compilation the `error` might be set describing the reason for
+ * the failed compilation. The caller takes ownership of the malloc()ed
+ * error.
+ *
+ * Requires there to be a current isolate.
+ */
+DART_EXPORT Dart_KernelCompilationResult
+Dart_CompileToKernel(const char* script_uri,
+                     const uint8_t* platform_kernel,
+                     const intptr_t platform_kernel_size,
+                     bool incremental_compile,
+                     bool snapshot_compile,
+                     const char* package_config,
+                     Dart_KernelCompilationVerbosityLevel verbosity);
+
+/**
+ * Compiles the given `script_uri` to a kernel file.
+ *
+ * \param platform_kernel A buffer containing the kernel of the platform (e.g.
+ * `vm_platform_strong.dill`). The VM does not take ownership of this memory.
+ *
+ * \param platform_kernel_size The length of the platform_kernel buffer.
+ *
+ * \param snapshot_compile Set to `true` when the compilation is for a snapshot.
+ * This is used by the frontend to determine if compilation related information
+ * should be printed to console (e.g., null safety mode).
+ *
+ * \param null_safety Provides null-safety mode setting for the compiler.
+ *
+ * \param verbosity Specifies the logging behavior of the kernel compilation
+ * service.
+ *
+ * \return Returns the result of the compilation.
+ *
+ * On a successful compilation the returned [Dart_KernelCompilationResult] has
+ * a status of [Dart_KernelCompilationStatus_Ok] and the `kernel`/`kernel_size`
+ * fields are set. The caller takes ownership of the malloc()ed buffer.
+ *
+ * On a failed compilation the `error` might be set describing the reason for
+ * the failed compilation. The caller takes ownership of the malloc()ed
+ * error.
+ */
+DART_EXPORT Dart_KernelCompilationResult
+Dart_CompileToKernelWithGivenNullsafety(
+    const char* script_uri,
+    const uint8_t* platform_kernel,
+    const intptr_t platform_kernel_size,
+    bool snapshot_compile,
+    const char* package_config,
+    const bool null_safety,
+    Dart_KernelCompilationVerbosityLevel verbosity);
+
+typedef struct {
+  const char* uri;
+  const char* source;
+} Dart_SourceFile;
+
+DART_EXPORT Dart_KernelCompilationResult Dart_KernelListDependencies(void);
+
+/**
+ * Sets the kernel buffer which will be used to load Dart SDK sources
+ * dynamically at runtime.
+ *
+ * \param platform_kernel A buffer containing kernel which has sources for the
+ * Dart SDK populated. Note: The VM does not take ownership of this memory.
+ *
+ * \param platform_kernel_size The length of the platform_kernel buffer.
+ */
+DART_EXPORT void Dart_SetDartLibrarySourcesKernel(
+    const uint8_t* platform_kernel,
+    const intptr_t platform_kernel_size);
+
+/**
+ * Detect the null safety opt-in status.
+ *
+ * When running from source, it is based on the opt-in status of `script_uri`.
+ * When running from a kernel buffer, it is based on the mode used when
+ *   generating `kernel_buffer`.
+ * When running from an appJIT or AOT snapshot, it is based on the mode used
+ *   when generating `snapshot_data`.
+ *
+ * \param script_uri Uri of the script that contains the source code
+ *
+ * \param package_config Uri of the package configuration file (either in format
+ *   of .packages or .dart_tool/package_config.json) for the null safety
+ *   detection to resolve package imports against. If this parameter is not
+ *   passed the package resolution of the parent isolate should be used.
+ *
+ * \param original_working_directory current working directory when the VM
+ *   process was launched, this is used to correctly resolve the path specified
+ *   for package_config.
+ *
+ * \param snapshot_data Buffer containing the snapshot data of the
+ *   isolate or NULL if no snapshot is provided. If provided, the buffers must
+ *   remain valid until the isolate shuts down.
+ *
+ * \param snapshot_instructions Buffer containing the snapshot instructions of
+ *   the isolate or NULL if no snapshot is provided. If provided, the buffers
+ *   must remain valid until the isolate shuts down.
+ *
+ * \param kernel_buffer A buffer which contains a kernel/DIL program. Must
+ *   remain valid until isolate shutdown.
+ *
+ * \param kernel_buffer_size The size of `kernel_buffer`.
+ *
+ * \return Returns true if the null safety is opted in by the input being
+ *   run `script_uri`, `snapshot_data` or `kernel_buffer`.
+ *
+ */
+DART_EXPORT bool Dart_DetectNullSafety(const char* script_uri,
+                                       const char* package_config,
+                                       const char* original_working_directory,
+                                       const uint8_t* snapshot_data,
+                                       const uint8_t* snapshot_instructions,
+                                       const uint8_t* kernel_buffer,
+                                       intptr_t kernel_buffer_size);
+
+#define DART_KERNEL_ISOLATE_NAME "kernel-service"
+
+/*
+ * =======
+ * Service
+ * =======
+ */
+
+#define DART_VM_SERVICE_ISOLATE_NAME "vm-service"
+
+/**
+ * Returns true if isolate is the service isolate.
+ *
+ * \param isolate An isolate
+ *
+ * \return Returns true if 'isolate' is the service isolate.
+ */
+DART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate);
+
+/**
+ * Writes the CPU profile to the timeline as a series of 'instant' events.
+ *
+ * Note that this is an expensive operation.
+ *
+ * \param main_port The main port of the Isolate whose profile samples to write.
+ * \param error An optional error, must be free()ed by caller.
+ *
+ * \return Returns true if the profile is successfully written and false
+ *         otherwise.
+ */
+DART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port, char** error);
+
+/*
+ * ==============
+ * Precompilation
+ * ==============
+ */
+
+/**
+ * Compiles all functions reachable from entry points and marks
+ * the isolate to disallow future compilation.
+ *
+ * Entry points should be specified using `@pragma("vm:entry-point")`
+ * annotation.
+ *
+ * \return An error handle if a compilation error or runtime error running const
+ * constructors was encountered.
+ */
+DART_EXPORT Dart_Handle Dart_Precompile(void);
+
+typedef void (*Dart_CreateLoadingUnitCallback)(
+    void* callback_data,
+    intptr_t loading_unit_id,
+    void** write_callback_data,
+    void** write_debug_callback_data);
+typedef void (*Dart_StreamingWriteCallback)(void* callback_data,
+                                            const uint8_t* buffer,
+                                            intptr_t size);
+typedef void (*Dart_StreamingCloseCallback)(void* callback_data);
+
+DART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id);
+
+// On Darwin systems, 'dlsym' adds an '_' to the beginning of the symbol name.
+// Use the '...CSymbol' definitions for resolving through 'dlsym'. The actual
+// symbol names in the objects are given by the '...AsmSymbol' definitions.
+#if defined(__APPLE__)
+#define kSnapshotBuildIdCSymbol "kDartSnapshotBuildId"
+#define kVmSnapshotDataCSymbol "kDartVmSnapshotData"
+#define kVmSnapshotInstructionsCSymbol "kDartVmSnapshotInstructions"
+#define kVmSnapshotBssCSymbol "kDartVmSnapshotBss"
+#define kIsolateSnapshotDataCSymbol "kDartIsolateSnapshotData"
+#define kIsolateSnapshotInstructionsCSymbol "kDartIsolateSnapshotInstructions"
+#define kIsolateSnapshotBssCSymbol "kDartIsolateSnapshotBss"
+#else
+#define kSnapshotBuildIdCSymbol "_kDartSnapshotBuildId"
+#define kVmSnapshotDataCSymbol "_kDartVmSnapshotData"
+#define kVmSnapshotInstructionsCSymbol "_kDartVmSnapshotInstructions"
+#define kVmSnapshotBssCSymbol "_kDartVmSnapshotBss"
+#define kIsolateSnapshotDataCSymbol "_kDartIsolateSnapshotData"
+#define kIsolateSnapshotInstructionsCSymbol "_kDartIsolateSnapshotInstructions"
+#define kIsolateSnapshotBssCSymbol "_kDartIsolateSnapshotBss"
+#endif
+
+#define kSnapshotBuildIdAsmSymbol "_kDartSnapshotBuildId"
+#define kVmSnapshotDataAsmSymbol "_kDartVmSnapshotData"
+#define kVmSnapshotInstructionsAsmSymbol "_kDartVmSnapshotInstructions"
+#define kVmSnapshotBssAsmSymbol "_kDartVmSnapshotBss"
+#define kIsolateSnapshotDataAsmSymbol "_kDartIsolateSnapshotData"
+#define kIsolateSnapshotInstructionsAsmSymbol                                  \
+  "_kDartIsolateSnapshotInstructions"
+#define kIsolateSnapshotBssAsmSymbol "_kDartIsolateSnapshotBss"
+
+/**
+ *  Creates a precompiled snapshot.
+ *   - A root library must have been loaded.
+ *   - Dart_Precompile must have been called.
+ *
+ *  Outputs an assembly file defining the symbols listed in the definitions
+ *  above.
+ *
+ *  The assembly should be compiled as a static or shared library and linked or
+ *  loaded by the embedder. Running this snapshot requires a VM compiled with
+ *  DART_PRECOMPILED_SNAPSHOT. The kDartVmSnapshotData and
+ *  kDartVmSnapshotInstructions should be passed to Dart_Initialize. The
+ *  kDartIsolateSnapshotData and kDartIsolateSnapshotInstructions should be
+ *  passed to Dart_CreateIsolateGroup.
+ *
+ *  The callback will be invoked one or more times to provide the assembly code.
+ *
+ *  If stripped is true, then the assembly code will not include DWARF
+ *  debugging sections.
+ *
+ *  If debug_callback_data is provided, debug_callback_data will be used with
+ *  the callback to provide separate debugging information.
+ *
+ *  \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
+                                    void* callback_data,
+                                    bool stripped,
+                                    void* debug_callback_data);
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateAppAOTSnapshotAsAssemblies(
+    Dart_CreateLoadingUnitCallback next_callback,
+    void* next_callback_data,
+    bool stripped,
+    Dart_StreamingWriteCallback write_callback,
+    Dart_StreamingCloseCallback close_callback);
+
+/**
+ *  Creates a precompiled snapshot.
+ *   - A root library must have been loaded.
+ *   - Dart_Precompile must have been called.
+ *
+ *  Outputs an ELF shared library defining the symbols
+ *   - _kDartVmSnapshotData
+ *   - _kDartVmSnapshotInstructions
+ *   - _kDartIsolateSnapshotData
+ *   - _kDartIsolateSnapshotInstructions
+ *
+ *  The shared library should be dynamically loaded by the embedder.
+ *  Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT.
+ *  The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to
+ *  Dart_Initialize. The kDartIsolateSnapshotData and
+ *  kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate.
+ *
+ *  The callback will be invoked one or more times to provide the binary output.
+ *
+ *  If stripped is true, then the binary output will not include DWARF
+ *  debugging sections.
+ *
+ *  If debug_callback_data is provided, debug_callback_data will be used with
+ *  the callback to provide separate debugging information.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,
+                               void* callback_data,
+                               bool stripped,
+                               void* debug_callback_data);
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateAppAOTSnapshotAsElfs(Dart_CreateLoadingUnitCallback next_callback,
+                                void* next_callback_data,
+                                bool stripped,
+                                Dart_StreamingWriteCallback write_callback,
+                                Dart_StreamingCloseCallback close_callback);
+
+/**
+ *  Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes
+ *  kDartVmSnapshotData and kDartVmSnapshotInstructions. It also does
+ *  not strip DWARF information from the generated assembly or allow for
+ *  separate debug information.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,
+                                   void* callback_data);
+
+/**
+ * Sorts the class-ids in depth first traversal order of the inheritance
+ * tree. This is a costly operation, but it can make method dispatch
+ * more efficient and is done before writing snapshots.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_SortClasses(void);
+
+/**
+ *  Creates a snapshot that caches compiled code and type feedback for faster
+ *  startup and quicker warmup in a subsequent process.
+ *
+ *  Outputs a snapshot in two pieces. The pieces should be passed to
+ *  Dart_CreateIsolateGroup in a VM using the same VM snapshot pieces used in the
+ *  current VM. The instructions piece must be loaded with read and execute
+ *  permissions; the data piece may be loaded as read-only.
+ *
+ *   - Requires the VM to have not been started with --precompilation.
+ *   - Not supported when targeting IA32.
+ *   - The VM writing the snapshot and the VM reading the snapshot must be the
+ *     same version, must be built in the same DEBUG/RELEASE/PRODUCT mode, must
+ *     be targeting the same architecture, and must both be in checked mode or
+ *     both in unchecked mode.
+ *
+ *  The buffers are scope allocated and are only valid until the next call to
+ *  Dart_ExitScope.
+ *
+ * \return A valid handle if no error occurs during the operation.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateAppJITSnapshotAsBlobs(uint8_t** isolate_snapshot_data_buffer,
+                                 intptr_t* isolate_snapshot_data_size,
+                                 uint8_t** isolate_snapshot_instructions_buffer,
+                                 intptr_t* isolate_snapshot_instructions_size);
+
+/**
+ * Like Dart_CreateAppJITSnapshotAsBlobs, but also creates a new VM snapshot.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_CreateCoreJITSnapshotAsBlobs(
+    uint8_t** vm_snapshot_data_buffer,
+    intptr_t* vm_snapshot_data_size,
+    uint8_t** vm_snapshot_instructions_buffer,
+    intptr_t* vm_snapshot_instructions_size,
+    uint8_t** isolate_snapshot_data_buffer,
+    intptr_t* isolate_snapshot_data_size,
+    uint8_t** isolate_snapshot_instructions_buffer,
+    intptr_t* isolate_snapshot_instructions_size);
+
+/**
+ * Get obfuscation map for precompiled code.
+ *
+ * Obfuscation map is encoded as a JSON array of pairs (original name,
+ * obfuscated name).
+ *
+ * \return Returns an error handler if the VM was built in a mode that does not
+ * support obfuscation.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle
+Dart_GetObfuscationMap(uint8_t** buffer, intptr_t* buffer_length);
+
+/**
+ *  Returns whether the VM only supports running from precompiled snapshots and
+ *  not from any other kind of snapshot or from source (that is, the VM was
+ *  compiled with DART_PRECOMPILED_RUNTIME).
+ */
+DART_EXPORT bool Dart_IsPrecompiledRuntime(void);
+
+/**
+ *  Print a native stack trace. Used for crash handling.
+ *
+ *  If context is NULL, prints the current stack trace. Otherwise, context
+ *  should be a CONTEXT* (Windows) or ucontext_t* (POSIX) from a signal handler
+ *  running on the current thread.
+ */
+DART_EXPORT void Dart_DumpNativeStackTrace(void* context);
+
+/**
+ *  Indicate that the process is about to abort, and the Dart VM should not
+ *  attempt to cleanup resources.
+ */
+DART_EXPORT void Dart_PrepareToAbort(void);
+
+#endif /* INCLUDE_DART_API_H_ */ /* NOLINT */
diff --git a/pkgs/jni/src/include/dart_api_dl.c b/pkgs/jni/src/include/dart_api_dl.c
new file mode 100644
index 0000000..c4a68f4
--- /dev/null
+++ b/pkgs/jni/src/include/dart_api_dl.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+#include "dart_api_dl.h"               /* NOLINT */
+#include "dart_version.h"              /* NOLINT */
+#include "internal/dart_api_dl_impl.h" /* NOLINT */
+
+#include <string.h>
+
+#define DART_API_DL_DEFINITIONS(name, R, A) name##_Type name##_DL = NULL;
+
+DART_API_ALL_DL_SYMBOLS(DART_API_DL_DEFINITIONS)
+
+#undef DART_API_DL_DEFINITIONS
+
+typedef void* DartApiEntry_function;
+
+DartApiEntry_function FindFunctionPointer(const DartApiEntry* entries,
+                                          const char* name) {
+  while (entries->name != NULL) {
+    if (strcmp(entries->name, name) == 0) return entries->function;
+    entries++;
+  }
+  return NULL;
+}
+
+intptr_t Dart_InitializeApiDL(void* data) {
+  DartApi* dart_api_data = (DartApi*)data;
+
+  if (dart_api_data->major != DART_API_DL_MAJOR_VERSION) {
+    // If the DartVM we're running on does not have the same version as this
+    // file was compiled against, refuse to initialize. The symbols are not
+    // compatible.
+    return -1;
+  }
+  // Minor versions are allowed to be different.
+  // If the DartVM has a higher minor version, it will provide more symbols
+  // than we initialize here.
+  // If the DartVM has a lower minor version, it will not provide all symbols.
+  // In that case, we leave the missing symbols un-initialized. Those symbols
+  // should not be used by the Dart and native code. The client is responsible
+  // for checking the minor version number himself based on which symbols it
+  // is using.
+  // (If we would error out on this case, recompiling native code against a
+  // newer SDK would break all uses on older SDKs, which is too strict.)
+
+  const DartApiEntry* dart_api_function_pointers = dart_api_data->functions;
+
+#define DART_API_DL_INIT(name, R, A)                                           \
+  name##_DL =                                                                  \
+      (name##_Type)(FindFunctionPointer(dart_api_function_pointers, #name));
+  DART_API_ALL_DL_SYMBOLS(DART_API_DL_INIT)
+#undef DART_API_DL_INIT
+
+  return 0;
+}
diff --git a/pkgs/jni/src/include/dart_api_dl.h b/pkgs/jni/src/include/dart_api_dl.h
new file mode 100644
index 0000000..804b281
--- /dev/null
+++ b/pkgs/jni/src/include/dart_api_dl.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+#ifndef RUNTIME_INCLUDE_DART_API_DL_H_
+#define RUNTIME_INCLUDE_DART_API_DL_H_
+
+#include "dart_api.h"        /* NOLINT */
+#include "dart_native_api.h" /* NOLINT */
+
+/** \mainpage Dynamically Linked Dart API
+ *
+ * This exposes a subset of symbols from dart_api.h and dart_native_api.h
+ * available in every Dart embedder through dynamic linking.
+ *
+ * All symbols are postfixed with _DL to indicate that they are dynamically
+ * linked and to prevent conflicts with the original symbol.
+ *
+ * Link `dart_api_dl.c` file into your library and invoke
+ * `Dart_InitializeApiDL` with `NativeApi.initializeApiDLData`.
+ */
+
+DART_EXPORT intptr_t Dart_InitializeApiDL(void* data);
+
+// ============================================================================
+// IMPORTANT! Never update these signatures without properly updating
+// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION.
+//
+// Verbatim copy of `dart_native_api.h` and `dart_api.h` symbol names and types
+// to trigger compile-time errors if the sybols in those files are updated
+// without updating these.
+//
+// Function return and argument types, and typedefs are carbon copied. Structs
+// are typechecked nominally in C/C++, so they are not copied, instead a
+// comment is added to their definition.
+typedef int64_t Dart_Port_DL;
+
+typedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id,
+                                             Dart_CObject* message);
+
+// dart_native_api.h symbols can be called on any thread.
+#define DART_NATIVE_API_DL_SYMBOLS(F)                                          \
+  /***** dart_native_api.h *****/                                              \
+  /* Dart_Port */                                                              \
+  F(Dart_PostCObject, bool, (Dart_Port_DL port_id, Dart_CObject * message))    \
+  F(Dart_PostInteger, bool, (Dart_Port_DL port_id, int64_t message))           \
+  F(Dart_NewNativePort, Dart_Port_DL,                                          \
+    (const char* name, Dart_NativeMessageHandler_DL handler,                   \
+     bool handle_concurrently))                                                \
+  F(Dart_CloseNativePort, bool, (Dart_Port_DL native_port_id))
+
+// dart_api.h symbols can only be called on Dart threads.
+#define DART_API_DL_SYMBOLS(F)                                                 \
+  /***** dart_api.h *****/                                                     \
+  /* Errors */                                                                 \
+  F(Dart_IsError, bool, (Dart_Handle handle))                                  \
+  F(Dart_IsApiError, bool, (Dart_Handle handle))                               \
+  F(Dart_IsUnhandledExceptionError, bool, (Dart_Handle handle))                \
+  F(Dart_IsCompilationError, bool, (Dart_Handle handle))                       \
+  F(Dart_IsFatalError, bool, (Dart_Handle handle))                             \
+  F(Dart_GetError, const char*, (Dart_Handle handle))                          \
+  F(Dart_ErrorHasException, bool, (Dart_Handle handle))                        \
+  F(Dart_ErrorGetException, Dart_Handle, (Dart_Handle handle))                 \
+  F(Dart_ErrorGetStackTrace, Dart_Handle, (Dart_Handle handle))                \
+  F(Dart_NewApiError, Dart_Handle, (const char* error))                        \
+  F(Dart_NewCompilationError, Dart_Handle, (const char* error))                \
+  F(Dart_NewUnhandledExceptionError, Dart_Handle, (Dart_Handle exception))     \
+  F(Dart_PropagateError, void, (Dart_Handle handle))                           \
+  /* Dart_Handle, Dart_PersistentHandle, Dart_WeakPersistentHandle */          \
+  F(Dart_HandleFromPersistent, Dart_Handle, (Dart_PersistentHandle object))    \
+  F(Dart_HandleFromWeakPersistent, Dart_Handle,                                \
+    (Dart_WeakPersistentHandle object))                                        \
+  F(Dart_NewPersistentHandle, Dart_PersistentHandle, (Dart_Handle object))     \
+  F(Dart_SetPersistentHandle, void,                                            \
+    (Dart_PersistentHandle obj1, Dart_Handle obj2))                            \
+  F(Dart_DeletePersistentHandle, void, (Dart_PersistentHandle object))         \
+  F(Dart_NewWeakPersistentHandle, Dart_WeakPersistentHandle,                   \
+    (Dart_Handle object, void* peer, intptr_t external_allocation_size,        \
+     Dart_HandleFinalizer callback))                                           \
+  F(Dart_DeleteWeakPersistentHandle, void, (Dart_WeakPersistentHandle object)) \
+  F(Dart_UpdateExternalSize, void,                                             \
+    (Dart_WeakPersistentHandle object, intptr_t external_allocation_size))     \
+  F(Dart_NewFinalizableHandle, Dart_FinalizableHandle,                         \
+    (Dart_Handle object, void* peer, intptr_t external_allocation_size,        \
+     Dart_HandleFinalizer callback))                                           \
+  F(Dart_DeleteFinalizableHandle, void,                                        \
+    (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object))         \
+  F(Dart_UpdateFinalizableExternalSize, void,                                  \
+    (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object,          \
+     intptr_t external_allocation_size))                                       \
+  /* Dart_Port */                                                              \
+  F(Dart_Post, bool, (Dart_Port_DL port_id, Dart_Handle object))               \
+  F(Dart_NewSendPort, Dart_Handle, (Dart_Port_DL port_id))                     \
+  F(Dart_SendPortGetId, Dart_Handle,                                           \
+    (Dart_Handle port, Dart_Port_DL * port_id))                                \
+  /* Scopes */                                                                 \
+  F(Dart_EnterScope, void, (void))                                             \
+  F(Dart_ExitScope, void, (void))
+
+#define DART_API_ALL_DL_SYMBOLS(F)                                             \
+  DART_NATIVE_API_DL_SYMBOLS(F)                                                \
+  DART_API_DL_SYMBOLS(F)
+// IMPORTANT! Never update these signatures without properly updating
+// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION.
+//
+// End of verbatim copy.
+// ============================================================================
+
+// Copy of definition of DART_EXPORT without 'used' attribute.
+//
+// The 'used' attribute cannot be used with DART_API_ALL_DL_SYMBOLS because
+// they are not function declarations, but variable declarations with a
+// function pointer type.
+//
+// The function pointer variables are initialized with the addresses of the
+// functions in the VM. If we were to use function declarations instead, we
+// would need to forward the call to the VM adding indirection.
+#if defined(__CYGWIN__)
+#error Tool chain and platform not supported.
+#elif defined(_WIN32)
+#if defined(DART_SHARED_LIB)
+#define DART_EXPORT_DL DART_EXTERN_C __declspec(dllexport)
+#else
+#define DART_EXPORT_DL DART_EXTERN_C
+#endif
+#else
+#if __GNUC__ >= 4
+#if defined(DART_SHARED_LIB)
+#define DART_EXPORT_DL DART_EXTERN_C __attribute__((visibility("default")))
+#else
+#define DART_EXPORT_DL DART_EXTERN_C
+#endif
+#else
+#error Tool chain not supported.
+#endif
+#endif
+
+#define DART_API_DL_DECLARATIONS(name, R, A)                                   \
+  typedef R(*name##_Type) A;                                                   \
+  DART_EXPORT_DL name##_Type name##_DL;
+
+DART_API_ALL_DL_SYMBOLS(DART_API_DL_DECLARATIONS)
+
+#undef DART_API_DL_DECLARATIONS
+
+#undef DART_EXPORT_DL
+
+#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */
diff --git a/pkgs/jni/src/include/dart_embedder_api.h b/pkgs/jni/src/include/dart_embedder_api.h
new file mode 100644
index 0000000..e565ebf
--- /dev/null
+++ b/pkgs/jni/src/include/dart_embedder_api.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef RUNTIME_INCLUDE_DART_EMBEDDER_API_H_
+#define RUNTIME_INCLUDE_DART_EMBEDDER_API_H_
+
+#include "include/dart_api.h"
+#include "include/dart_tools_api.h"
+
+namespace dart {
+namespace embedder {
+
+// Initialize all subsystems of the embedder.
+//
+// Must be called before the `Dart_Initialize()` call to initialize the
+// Dart VM.
+//
+// Returns true on success and false otherwise, in which case error would
+// contain error message.
+DART_WARN_UNUSED_RESULT bool InitOnce(char** error);
+
+// Cleans up all subsystems of the embedder.
+//
+// Must be called after the `Dart_Cleanup()` call to initialize the
+// Dart VM.
+void Cleanup();
+
+// Common arguments that are passed to isolate creation callback and to
+// API methods that create isolates.
+struct IsolateCreationData {
+  // URI for the main script that will be running in the isolate.
+  const char* script_uri;
+
+  // Advisory name of the main method that will be run by isolate.
+  // Only used for error messages.
+  const char* main;
+
+  // Isolate creation flags. Might be absent.
+  Dart_IsolateFlags* flags;
+
+  // Isolate group callback data.
+  void* isolate_group_data;
+
+  // Isolate callback data.
+  void* isolate_data;
+};
+
+// Create and initialize kernel-service isolate. This method should be used
+// when VM invokes isolate creation callback with DART_KERNEL_ISOLATE_NAME as
+// script_uri.
+// The isolate is created from the given snapshot (might be kernel data or
+// app-jit snapshot).
+DART_WARN_UNUSED_RESULT Dart_Isolate
+CreateKernelServiceIsolate(const IsolateCreationData& data,
+                           const uint8_t* buffer,
+                           intptr_t buffer_size,
+                           char** error);
+
+// Service isolate configuration.
+struct VmServiceConfiguration {
+  enum {
+    kBindHttpServerToAFreePort = 0,
+    kDoNotAutoStartHttpServer = -1
+  };
+
+  // Address to which HTTP server will be bound.
+  const char* ip;
+
+  // Default port. See enum above for special values.
+  int port;
+
+  // If non-null, connection information for the VM service will be output to a
+  // file in JSON format at the location specified.
+  const char* write_service_info_filename;
+
+  // TODO(vegorov) document these ones.
+  bool dev_mode;
+  bool deterministic;
+  bool disable_auth_codes;
+};
+
+// Create and initialize vm-service isolate from the given AOT snapshot, which
+// is expected to contain all necessary 'vm-service' libraries.
+// This method should be used when VM invokes isolate creation callback with
+// DART_VM_SERVICE_ISOLATE_NAME as script_uri.
+DART_WARN_UNUSED_RESULT Dart_Isolate
+CreateVmServiceIsolate(const IsolateCreationData& data,
+                       const VmServiceConfiguration& config,
+                       const uint8_t* isolate_data,
+                       const uint8_t* isolate_instr,
+                       char** error);
+
+// Create and initialize vm-service isolate from the given kernel binary, which
+// is expected to contain all necessary 'vm-service' libraries.
+// This method should be used when VM invokes isolate creation callback with
+// DART_VM_SERVICE_ISOLATE_NAME as script_uri.
+DART_WARN_UNUSED_RESULT Dart_Isolate
+CreateVmServiceIsolateFromKernel(const IsolateCreationData& data,
+                                 const VmServiceConfiguration& config,
+                                 const uint8_t* kernel_buffer,
+                                 intptr_t kernel_buffer_size,
+                                 char** error);
+
+}  // namespace embedder
+}  // namespace dart
+
+#endif  // RUNTIME_INCLUDE_DART_EMBEDDER_API_H_
diff --git a/pkgs/jni/src/include/dart_native_api.h b/pkgs/jni/src/include/dart_native_api.h
new file mode 100644
index 0000000..318a4b7
--- /dev/null
+++ b/pkgs/jni/src/include/dart_native_api.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+#ifndef RUNTIME_INCLUDE_DART_NATIVE_API_H_
+#define RUNTIME_INCLUDE_DART_NATIVE_API_H_
+
+#include "dart_api.h" /* NOLINT */
+
+/*
+ * ==========================================
+ * Message sending/receiving from native code
+ * ==========================================
+ */
+
+/**
+ * A Dart_CObject is used for representing Dart objects as native C
+ * data outside the Dart heap. These objects are totally detached from
+ * the Dart heap. Only a subset of the Dart objects have a
+ * representation as a Dart_CObject.
+ *
+ * The string encoding in the 'value.as_string' is UTF-8.
+ *
+ * All the different types from dart:typed_data are exposed as type
+ * kTypedData. The specific type from dart:typed_data is in the type
+ * field of the as_typed_data structure. The length in the
+ * as_typed_data structure is always in bytes.
+ *
+ * The data for kTypedData is copied on message send and ownership remains with
+ * the caller. The ownership of data for kExternalTyped is passed to the VM on
+ * message send and returned when the VM invokes the
+ * Dart_HandleFinalizer callback; a non-NULL callback must be provided.
+ *
+ * Note that Dart_CObject_kNativePointer is intended for internal use by
+ * dart:io implementation and has no connection to dart:ffi Pointer class.
+ * It represents a pointer to a native resource of a known type.
+ * The receiving side will only see this pointer as an integer and will not
+ * see the specified finalizer.
+ * The specified finalizer will only be invoked if the message is not delivered.
+ */
+typedef enum {
+  Dart_CObject_kNull = 0,
+  Dart_CObject_kBool,
+  Dart_CObject_kInt32,
+  Dart_CObject_kInt64,
+  Dart_CObject_kDouble,
+  Dart_CObject_kString,
+  Dart_CObject_kArray,
+  Dart_CObject_kTypedData,
+  Dart_CObject_kExternalTypedData,
+  Dart_CObject_kUnmodifiableExternalTypedData,
+  Dart_CObject_kSendPort,
+  Dart_CObject_kCapability,
+  Dart_CObject_kNativePointer,
+  Dart_CObject_kUnsupported,
+  Dart_CObject_kNumberOfTypes
+} Dart_CObject_Type;
+
+typedef struct _Dart_CObject {
+  Dart_CObject_Type type;
+  union {
+    bool as_bool;
+    int32_t as_int32;
+    int64_t as_int64;
+    double as_double;
+    char* as_string;
+    struct {
+      Dart_Port id;
+      Dart_Port origin_id;
+    } as_send_port;
+    struct {
+      int64_t id;
+    } as_capability;
+    struct {
+      intptr_t length;
+      struct _Dart_CObject** values;
+    } as_array;
+    struct {
+      Dart_TypedData_Type type;
+      intptr_t length; /* in elements, not bytes */
+      const uint8_t* values;
+    } as_typed_data;
+    struct {
+      Dart_TypedData_Type type;
+      intptr_t length; /* in elements, not bytes */
+      uint8_t* data;
+      void* peer;
+      Dart_HandleFinalizer callback;
+    } as_external_typed_data;
+    struct {
+      intptr_t ptr;
+      intptr_t size;
+      Dart_HandleFinalizer callback;
+    } as_native_pointer;
+  } value;
+} Dart_CObject;
+// This struct is versioned by DART_API_DL_MAJOR_VERSION, bump the version when
+// changing this struct.
+
+/**
+ * Posts a message on some port. The message will contain the Dart_CObject
+ * object graph rooted in 'message'.
+ *
+ * While the message is being sent the state of the graph of Dart_CObject
+ * structures rooted in 'message' should not be accessed, as the message
+ * generation will make temporary modifications to the data. When the message
+ * has been sent the graph will be fully restored.
+ *
+ * If true is returned, the message was enqueued, and finalizers for external
+ * typed data will eventually run, even if the receiving isolate shuts down
+ * before processing the message. If false is returned, the message was not
+ * enqueued and ownership of external typed data in the message remains with the
+ * caller.
+ *
+ * This function may be called on any thread when the VM is running (that is,
+ * after Dart_Initialize has returned and before Dart_Cleanup has been called).
+ *
+ * \param port_id The destination port.
+ * \param message The message to send.
+ *
+ * \return True if the message was posted.
+ */
+DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message);
+
+/**
+ * Posts a message on some port. The message will contain the integer 'message'.
+ *
+ * \param port_id The destination port.
+ * \param message The message to send.
+ *
+ * \return True if the message was posted.
+ */
+DART_EXPORT bool Dart_PostInteger(Dart_Port port_id, int64_t message);
+
+/**
+ * A native message handler.
+ *
+ * This handler is associated with a native port by calling
+ * Dart_NewNativePort.
+ *
+ * The message received is decoded into the message structure. The
+ * lifetime of the message data is controlled by the caller. All the
+ * data references from the message are allocated by the caller and
+ * will be reclaimed when returning to it.
+ */
+typedef void (*Dart_NativeMessageHandler)(Dart_Port dest_port_id,
+                                          Dart_CObject* message);
+
+/**
+ * Creates a new native port.  When messages are received on this
+ * native port, then they will be dispatched to the provided native
+ * message handler.
+ *
+ * \param name The name of this port in debugging messages.
+ * \param handler The C handler to run when messages arrive on the port.
+ * \param handle_concurrently Is it okay to process requests on this
+ *                            native port concurrently?
+ *
+ * \return If successful, returns the port id for the native port.  In
+ *   case of error, returns ILLEGAL_PORT.
+ */
+DART_EXPORT Dart_Port Dart_NewNativePort(const char* name,
+                                         Dart_NativeMessageHandler handler,
+                                         bool handle_concurrently);
+/* TODO(turnidge): Currently handle_concurrently is ignored. */
+
+/**
+ * Closes the native port with the given id.
+ *
+ * The port must have been allocated by a call to Dart_NewNativePort.
+ *
+ * \param native_port_id The id of the native port to close.
+ *
+ * \return Returns true if the port was closed successfully.
+ */
+DART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id);
+
+/*
+ * ==================
+ * Verification Tools
+ * ==================
+ */
+
+/**
+ * Forces all loaded classes and functions to be compiled eagerly in
+ * the current isolate..
+ *
+ * TODO(turnidge): Document.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_CompileAll(void);
+
+/**
+ * Finalizes all classes.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_FinalizeAllClasses(void);
+
+/*  This function is intentionally undocumented.
+ *
+ *  It should not be used outside internal tests.
+ */
+DART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg);
+
+#endif /* INCLUDE_DART_NATIVE_API_H_ */ /* NOLINT */
diff --git a/pkgs/jni/src/include/dart_tools_api.h b/pkgs/jni/src/include/dart_tools_api.h
new file mode 100644
index 0000000..90687f6
--- /dev/null
+++ b/pkgs/jni/src/include/dart_tools_api.h
@@ -0,0 +1,620 @@
+// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef RUNTIME_INCLUDE_DART_TOOLS_API_H_
+#define RUNTIME_INCLUDE_DART_TOOLS_API_H_
+
+#include "dart_api.h" /* NOLINT */
+
+/** \mainpage Dart Tools Embedding API Reference
+ *
+ * This reference describes the Dart embedding API for tools. Tools include
+ * a debugger, service protocol, and timeline.
+ *
+ * NOTE: The APIs described in this file are unstable and subject to change.
+ *
+ * This reference is generated from the header include/dart_tools_api.h.
+ */
+
+/*
+ * ========
+ * Debugger
+ * ========
+ */
+
+/**
+ * ILLEGAL_ISOLATE_ID is a number guaranteed never to be associated with a
+ * valid isolate.
+ */
+#define ILLEGAL_ISOLATE_ID ILLEGAL_PORT
+
+/**
+ * ILLEGAL_ISOLATE_GROUP_ID is a number guaranteed never to be associated with a
+ * valid isolate group.
+ */
+#define ILLEGAL_ISOLATE_GROUP_ID 0
+
+/*
+ * =======
+ * Service
+ * =======
+ */
+
+/**
+ * A service request callback function.
+ *
+ * These callbacks, registered by the embedder, are called when the VM receives
+ * a service request it can't handle and the service request command name
+ * matches one of the embedder registered handlers.
+ *
+ * The return value of the callback indicates whether the response
+ * should be used as a regular result or an error result.
+ * Specifically, if the callback returns true, a regular JSON-RPC
+ * response is built in the following way:
+ *
+ * {
+ *   "jsonrpc": "2.0",
+ *   "result": <json_object>,
+ *   "id": <some sequence id>,
+ * }
+ *
+ * If the callback returns false, a JSON-RPC error is built like this:
+ *
+ * {
+ *   "jsonrpc": "2.0",
+ *   "error": <json_object>,
+ *   "id": <some sequence id>,
+ * }
+ *
+ * \param method The rpc method name.
+ * \param param_keys Service requests can have key-value pair parameters. The
+ *   keys and values are flattened and stored in arrays.
+ * \param param_values The values associated with the keys.
+ * \param num_params The length of the param_keys and param_values arrays.
+ * \param user_data The user_data pointer registered with this handler.
+ * \param result A C string containing a valid JSON object. The returned
+ *   pointer will be freed by the VM by calling free.
+ *
+ * \return True if the result is a regular JSON-RPC response, false if the
+ *   result is a JSON-RPC error.
+ */
+typedef bool (*Dart_ServiceRequestCallback)(const char* method,
+                                            const char** param_keys,
+                                            const char** param_values,
+                                            intptr_t num_params,
+                                            void* user_data,
+                                            const char** json_object);
+
+/**
+ * Register a Dart_ServiceRequestCallback to be called to handle
+ * requests for the named rpc on a specific isolate. The callback will
+ * be invoked with the current isolate set to the request target.
+ *
+ * \param method The name of the method that this callback is responsible for.
+ * \param callback The callback to invoke.
+ * \param user_data The user data passed to the callback.
+ *
+ * NOTE: If multiple callbacks with the same name are registered, only
+ * the last callback registered will be remembered.
+ */
+DART_EXPORT void Dart_RegisterIsolateServiceRequestCallback(
+    const char* method,
+    Dart_ServiceRequestCallback callback,
+    void* user_data);
+
+/**
+ * Register a Dart_ServiceRequestCallback to be called to handle
+ * requests for the named rpc. The callback will be invoked without a
+ * current isolate.
+ *
+ * \param method The name of the command that this callback is responsible for.
+ * \param callback The callback to invoke.
+ * \param user_data The user data passed to the callback.
+ *
+ * NOTE: If multiple callbacks with the same name are registered, only
+ * the last callback registered will be remembered.
+ */
+DART_EXPORT void Dart_RegisterRootServiceRequestCallback(
+    const char* method,
+    Dart_ServiceRequestCallback callback,
+    void* user_data);
+
+/**
+ * Embedder information which can be requested by the VM for internal or
+ * reporting purposes.
+ *
+ * The pointers in this structure are not going to be cached or freed by the VM.
+ */
+
+ #define DART_EMBEDDER_INFORMATION_CURRENT_VERSION (0x00000001)
+
+typedef struct {
+  int32_t version;
+  const char* name;  // [optional] The name of the embedder
+  int64_t current_rss;  // [optional] the current RSS of the embedder
+  int64_t max_rss;  // [optional] the maximum RSS of the embedder
+} Dart_EmbedderInformation;
+
+/**
+ * Callback provided by the embedder that is used by the VM to request
+ * information.
+ *
+ * \return Returns a pointer to a Dart_EmbedderInformation structure.
+ * The embedder keeps the ownership of the structure and any field in it.
+ * The embedder must ensure that the structure will remain valid until the
+ * next invokation of the callback.
+ */
+typedef void (*Dart_EmbedderInformationCallback)(
+    Dart_EmbedderInformation* info);
+
+/**
+ * Register a Dart_ServiceRequestCallback to be called to handle
+ * requests for the named rpc. The callback will be invoked without a
+ * current isolate.
+ *
+ * \param method The name of the command that this callback is responsible for.
+ * \param callback The callback to invoke.
+ * \param user_data The user data passed to the callback.
+ *
+ * NOTE: If multiple callbacks are registered, only the last callback registered
+ * will be remembered.
+ */
+DART_EXPORT void Dart_SetEmbedderInformationCallback(
+    Dart_EmbedderInformationCallback callback);
+
+/**
+ * Invoke a vm-service method and wait for its result.
+ *
+ * \param request_json The utf8-encoded json-rpc request.
+ * \param request_json_length The length of the json-rpc request.
+ *
+ * \param response_json The returned utf8-encoded json response, must be
+ *   free()ed by caller.
+ * \param response_json_length The length of the returned json response.
+ * \param error An optional error, must be free()ed by caller.
+ *
+ * \return Whether the call was successfully performed.
+ *
+ * NOTE: This method does not need a current isolate and must not have the
+ * vm-isolate being the current isolate. It must be called after
+ * Dart_Initialize() and before Dart_Cleanup().
+ */
+DART_EXPORT bool Dart_InvokeVMServiceMethod(uint8_t* request_json,
+                                            intptr_t request_json_length,
+                                            uint8_t** response_json,
+                                            intptr_t* response_json_length,
+                                            char** error);
+
+/*
+ * ========
+ * Event Streams
+ * ========
+ */
+
+/**
+ * A callback invoked when the VM service gets a request to listen to
+ * some stream.
+ *
+ * \return Returns true iff the embedder supports the named stream id.
+ */
+typedef bool (*Dart_ServiceStreamListenCallback)(const char* stream_id);
+
+/**
+ * A callback invoked when the VM service gets a request to cancel
+ * some stream.
+ */
+typedef void (*Dart_ServiceStreamCancelCallback)(const char* stream_id);
+
+/**
+ * Adds VM service stream callbacks.
+ *
+ * \param listen_callback A function pointer to a listen callback function.
+ *   A listen callback function should not be already set when this function
+ *   is called. A NULL value removes the existing listen callback function
+ *   if any.
+ *
+ * \param cancel_callback A function pointer to a cancel callback function.
+ *   A cancel callback function should not be already set when this function
+ *   is called. A NULL value removes the existing cancel callback function
+ *   if any.
+ *
+ * \return Success if the callbacks were added.  Otherwise, returns an
+ *   error handle.
+ */
+DART_EXPORT char* Dart_SetServiceStreamCallbacks(
+    Dart_ServiceStreamListenCallback listen_callback,
+    Dart_ServiceStreamCancelCallback cancel_callback);
+
+/**
+ * Sends a data event to clients of the VM Service.
+ *
+ * A data event is used to pass an array of bytes to subscribed VM
+ * Service clients.  For example, in the standalone embedder, this is
+ * function used to provide WriteEvents on the Stdout and Stderr
+ * streams.
+ *
+ * If the embedder passes in a stream id for which no client is
+ * subscribed, then the event is ignored.
+ *
+ * \param stream_id The id of the stream on which to post the event.
+ *
+ * \param event_kind A string identifying what kind of event this is.
+ *   For example, 'WriteEvent'.
+ *
+ * \param bytes A pointer to an array of bytes.
+ *
+ * \param bytes_length The length of the byte array.
+ *
+ * \return NULL if the arguments are well formed.  Otherwise, returns an
+ *   error string. The caller is responsible for freeing the error message.
+ */
+DART_EXPORT char* Dart_ServiceSendDataEvent(const char* stream_id,
+                                            const char* event_kind,
+                                            const uint8_t* bytes,
+                                            intptr_t bytes_length);
+
+/**
+ * Usage statistics for a space/generation at a particular moment in time.
+ *
+ * \param used Amount of memory used, in bytes.
+ *
+ * \param capacity Memory capacity, in bytes.
+ *
+ * \param external External memory, in bytes.
+ *
+ * \param collections How many times the garbage collector has run in this
+ *   space.
+ *
+ * \param time Cumulative time spent collecting garbage in this space, in
+ *   seconds.
+ *
+ * \param avg_collection_period Average time between garbage collector running
+ *   in this space, in milliseconds.
+ */
+typedef struct {
+  intptr_t used;
+  intptr_t capacity;
+  intptr_t external;
+  intptr_t collections;
+  double time;
+  double avg_collection_period;
+} Dart_GCStats;
+
+/**
+ * A Garbage Collection event with memory usage statistics.
+ *
+ * \param type The event type. Static lifetime.
+ *
+ * \param reason The reason for the GC event. Static lifetime.
+ *
+ * \param new_space Data for New Space.
+ *
+ * \param old_space Data for Old Space.
+ */
+typedef struct {
+  const char* type;
+  const char* reason;
+
+  Dart_IsolateGroupId isolate_group_id;
+
+  Dart_GCStats new_space;
+  Dart_GCStats old_space;
+} Dart_GCEvent;
+
+/**
+ * A callback invoked when the VM emits a GC event.
+ *
+ * \param event The GC event data. Pointer only valid for the duration of the
+ *   callback.
+ */
+typedef void (*Dart_GCEventCallback)(Dart_GCEvent* event);
+
+/**
+ * Sets the native GC event callback.
+ *
+ * \param callback A function pointer to an event handler callback function.
+ *   A NULL value removes the existing listen callback function if any.
+ */
+DART_EXPORT void Dart_SetGCEventCallback(Dart_GCEventCallback callback);
+
+/*
+ * ========
+ * Reload support
+ * ========
+ *
+ * These functions are used to implement reloading in the Dart VM.
+ * This is an experimental feature, so embedders should be prepared
+ * for these functions to change.
+ */
+
+/**
+ * A callback which determines whether the file at some url has been
+ * modified since some time.  If the file cannot be found, true should
+ * be returned.
+ */
+typedef bool (*Dart_FileModifiedCallback)(const char* url, int64_t since);
+
+DART_EXPORT char* Dart_SetFileModifiedCallback(
+    Dart_FileModifiedCallback file_modified_callback);
+
+/**
+ * Returns true if isolate is currently reloading.
+ */
+DART_EXPORT bool Dart_IsReloading();
+
+/*
+ * ========
+ * Timeline
+ * ========
+ */
+
+/**
+ * Enable tracking of specified timeline category. This is operational
+ * only when systrace timeline functionality is turned on.
+ *
+ * \param categories A comma separated list of categories that need to
+ *   be enabled, the categories are
+ *   "all" : All categories
+ *   "API" - Execution of Dart C API functions
+ *   "Compiler" - Execution of Dart JIT compiler
+ *   "CompilerVerbose" - More detailed Execution of Dart JIT compiler
+ *   "Dart" - Execution of Dart code
+ *   "Debugger" - Execution of Dart debugger
+ *   "Embedder" - Execution of Dart embedder code
+ *   "GC" - Execution of Dart Garbage Collector
+ *   "Isolate" - Dart Isolate lifecycle execution
+ *   "VM" - Excution in Dart VM runtime code
+ *   "" - None
+ *
+ *  When "all" is specified all the categories are enabled.
+ *  When a comma separated list of categories is specified, the categories
+ *   that are specified will be enabled and the rest will be disabled. 
+ *  When "" is specified all the categories are disabled.
+ *  The category names are case sensitive.
+ *  eg:  Dart_EnableTimelineCategory("all");
+ *       Dart_EnableTimelineCategory("GC,API,Isolate");
+ *       Dart_EnableTimelineCategory("GC,Debugger,Dart");
+ *
+ * \return True if the categories were successfully enabled, False otherwise.
+ */
+DART_EXPORT bool Dart_SetEnabledTimelineCategory(const char* categories);
+
+/**
+ * Returns a timestamp in microseconds. This timestamp is suitable for
+ * passing into the timeline system, and uses the same monotonic clock
+ * as dart:developer's Timeline.now.
+ *
+ * \return A timestamp that can be passed to the timeline system.
+ */
+DART_EXPORT int64_t Dart_TimelineGetMicros();
+
+/**
+ * Returns a raw timestamp in from the monotonic clock.
+ *
+ * \return A raw timestamp from the monotonic clock.
+ */
+DART_EXPORT int64_t Dart_TimelineGetTicks();
+
+/**
+ * Returns the frequency of the monotonic clock.
+ *
+ * \return The frequency of the monotonic clock.
+ */
+DART_EXPORT int64_t Dart_TimelineGetTicksFrequency();
+
+typedef enum {
+  Dart_Timeline_Event_Begin,          // Phase = 'B'.
+  Dart_Timeline_Event_End,            // Phase = 'E'.
+  Dart_Timeline_Event_Instant,        // Phase = 'i'.
+  Dart_Timeline_Event_Duration,       // Phase = 'X'.
+  Dart_Timeline_Event_Async_Begin,    // Phase = 'b'.
+  Dart_Timeline_Event_Async_End,      // Phase = 'e'.
+  Dart_Timeline_Event_Async_Instant,  // Phase = 'n'.
+  Dart_Timeline_Event_Counter,        // Phase = 'C'.
+  Dart_Timeline_Event_Flow_Begin,     // Phase = 's'.
+  Dart_Timeline_Event_Flow_Step,      // Phase = 't'.
+  Dart_Timeline_Event_Flow_End,       // Phase = 'f'.
+} Dart_Timeline_Event_Type;
+
+/**
+ * Add a timeline event to the embedder stream.
+ *
+ * \param label The name of the event. Its lifetime must extend at least until
+ *     Dart_Cleanup.
+ * \param timestamp0 The first timestamp of the event.
+ * \param timestamp1_or_async_id The second timestamp of the event or
+ *     the async id.
+ * \param argument_count The number of argument names and values.
+ * \param argument_names An array of names of the arguments. The lifetime of the
+ *     names must extend at least until Dart_Cleanup. The array may be reclaimed
+ *     when this call returns.
+ * \param argument_values An array of values of the arguments. The values and
+ *     the array may be reclaimed when this call returns.
+ */
+DART_EXPORT void Dart_TimelineEvent(const char* label,
+                                    int64_t timestamp0,
+                                    int64_t timestamp1_or_async_id,
+                                    Dart_Timeline_Event_Type type,
+                                    intptr_t argument_count,
+                                    const char** argument_names,
+                                    const char** argument_values);
+
+/**
+ * Associates a name with the current thread. This name will be used to name
+ * threads in the timeline. Can only be called after a call to Dart_Initialize.
+ *
+ * \param name The name of the thread.
+ */
+DART_EXPORT void Dart_SetThreadName(const char* name);
+
+typedef struct {
+  const char* name;
+  const char* value;
+} Dart_TimelineRecorderEvent_Argument;
+
+#define DART_TIMELINE_RECORDER_CURRENT_VERSION (0x00000001)
+
+typedef struct {
+  /* Set to DART_TIMELINE_RECORDER_CURRENT_VERSION */
+  int32_t version;
+
+  /* The event's type / phase. */
+  Dart_Timeline_Event_Type type;
+
+  /* The event's timestamp according to the same clock as
+   * Dart_TimelineGetMicros. For a duration event, this is the beginning time.
+   */
+  int64_t timestamp0;
+
+  /* For a duration event, this is the end time. For an async event, this is the
+   * async id. */
+  int64_t timestamp1_or_async_id;
+
+  /* The current isolate of the event, as if by Dart_GetMainPortId, or
+   * ILLEGAL_PORT if the event had no current isolate. */
+  Dart_Port isolate;
+
+  /* The current isolate group of the event, as if by
+   * Dart_CurrentIsolateGroupId, or ILLEGAL_PORT if the event had no current
+   * isolate group. */
+  Dart_IsolateGroupId isolate_group;
+
+  /* The name / label of the event. */
+  const char* label;
+
+  /* The stream / category of the event. */
+  const char* stream;
+
+  intptr_t argument_count;
+  Dart_TimelineRecorderEvent_Argument* arguments;
+} Dart_TimelineRecorderEvent;
+
+/**
+ * Callback provided by the embedder to handle the completion of timeline
+ * events.
+ *
+ * \param event A timeline event that has just been completed. The VM keeps
+ * ownership of the event and any field in it (i.e., the embedder should copy
+ * any values it needs after the callback returns).
+ */
+typedef void (*Dart_TimelineRecorderCallback)(
+    Dart_TimelineRecorderEvent* event);
+
+/**
+ * Register a `Dart_TimelineRecorderCallback` to be called as timeline events
+ * are completed.
+ *
+ * The callback will be invoked without a current isolate.
+ *
+ * The callback will be invoked on the thread completing the event. Because
+ * `Dart_TimelineEvent` may be called by any thread, the callback may be called
+ * on any thread.
+ *
+ * The callback may be invoked at any time after `Dart_Initialize` is called and
+ * before `Dart_Cleanup` returns.
+ *
+ * If multiple callbacks are registered, only the last callback registered
+ * will be remembered. Providing a NULL callback will clear the registration
+ * (i.e., a NULL callback produced a no-op instead of a crash).
+ *
+ * Setting a callback is insuffient to receive events through the callback. The
+ * VM flag `timeline_recorder` must also be set to `callback`.
+ */
+DART_EXPORT void Dart_SetTimelineRecorderCallback(
+    Dart_TimelineRecorderCallback callback);
+
+/*
+ * =======
+ * Metrics
+ * =======
+ */
+
+/**
+ * Return metrics gathered for the VM and individual isolates.
+ *
+ * NOTE: Non-heap metrics are not available in PRODUCT builds of Dart.
+ * Calling the non-heap metric functions on a PRODUCT build might return invalid metrics.
+ */
+DART_EXPORT int64_t Dart_VMIsolateCountMetric();  // Counter
+DART_EXPORT int64_t Dart_VMCurrentRSSMetric();    // Byte
+DART_EXPORT int64_t Dart_VMPeakRSSMetric();       // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapOldUsedMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapOldUsedMaxMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapOldCapacityMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapOldCapacityMaxMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapOldExternalMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapNewUsedMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapNewUsedMaxMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapNewCapacityMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapNewCapacityMaxMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapNewExternalMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapGlobalUsedMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateGroupHeapGlobalUsedMaxMetric(Dart_IsolateGroup group);  // Byte
+DART_EXPORT int64_t
+Dart_IsolateRunnableLatencyMetric(Dart_Isolate isolate);  // Microsecond
+DART_EXPORT int64_t
+Dart_IsolateRunnableHeapSizeMetric(Dart_Isolate isolate);  // Byte
+
+/*
+ * ========
+ * UserTags
+ * ========
+ */
+
+/*
+ * Gets the current isolate's currently set UserTag instance.
+ *
+ * \return The currently set UserTag instance.
+ */
+DART_EXPORT Dart_Handle Dart_GetCurrentUserTag();
+
+/*
+ * Gets the current isolate's default UserTag instance.
+ *
+ * \return The default UserTag with label 'Default'
+ */
+DART_EXPORT Dart_Handle Dart_GetDefaultUserTag();
+
+/*
+ * Creates a new UserTag instance.
+ *
+ * \param label The name of the new UserTag.
+ *
+ * \return The newly created UserTag instance or an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_NewUserTag(const char* label);
+
+/*
+ * Updates the current isolate's UserTag to a new value.
+ *
+ * \param user_tag The UserTag to be set as the current UserTag.
+ *
+ * \return The previously set UserTag instance or an error handle.
+ */
+DART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag);
+
+/*
+ * Returns the label of a given UserTag instance.
+ *
+ * \param user_tag The UserTag from which the label will be retrieved.
+ *
+ * \return The UserTag's label. NULL if the user_tag is invalid. The caller is
+ *   responsible for freeing the returned label.
+ */
+DART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_GetUserTagLabel(
+    Dart_Handle user_tag);
+
+#endif  // RUNTIME_INCLUDE_DART_TOOLS_API_H_
diff --git a/pkgs/jni/src/include/dart_version.h b/pkgs/jni/src/include/dart_version.h
new file mode 100644
index 0000000..b3b4924
--- /dev/null
+++ b/pkgs/jni/src/include/dart_version.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+#ifndef RUNTIME_INCLUDE_DART_VERSION_H_
+#define RUNTIME_INCLUDE_DART_VERSION_H_
+
+// On breaking changes the major version is increased.
+// On backwards compatible changes the minor version is increased.
+// The versioning covers the symbols exposed in dart_api_dl.h
+#define DART_API_DL_MAJOR_VERSION 2
+#define DART_API_DL_MINOR_VERSION 0
+
+#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */
diff --git a/pkgs/jni/src/include/internal/dart_api_dl_impl.h b/pkgs/jni/src/include/internal/dart_api_dl_impl.h
new file mode 100644
index 0000000..e4a5689
--- /dev/null
+++ b/pkgs/jni/src/include/internal/dart_api_dl_impl.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+#ifndef RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_
+#define RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_
+
+typedef struct {
+  const char* name;
+  void (*function)(void);
+} DartApiEntry;
+
+typedef struct {
+  const int major;
+  const int minor;
+  const DartApiEntry* const functions;
+} DartApi;
+
+#endif /* RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ */ /* NOLINT */
diff --git a/pkgs/jni/test/jni_test.dart b/pkgs/jni/test/jni_test.dart
index bbf437b..a40ba81 100644
--- a/pkgs/jni/test/jni_test.dart
+++ b/pkgs/jni/test/jni_test.dart
@@ -40,6 +40,10 @@
   // For examples of a higher level API, see `jni_object_tests.dart`.
   final env = Jni.env;
 
+  test('initDLApi', () {
+    Jni.initDLApi();
+  });
+
   test('get JNI Version', () {
     expect(Jni.env.GetVersion(), isNot(equals(0)));
   });
diff --git a/pkgs/jnigen/README.md b/pkgs/jnigen/README.md
index b5d1bff..d599e7c 100644
--- a/pkgs/jnigen/README.md
+++ b/pkgs/jnigen/README.md
@@ -196,6 +196,7 @@
 | `source_path`          | List of directory paths | Directories to search for source files. Note: source_path for dependencies downloaded using `maven_downloads` configuration is added automatically without the need to specify here. |
 | `class_path`           | List of directory / JAR paths | Classpath for API summary generation. This should include any JAR dependencies of the source files in `source_path`. |
 | `classes` *            | List of qualified class / package names | List of qualified class / package names. `source_path` will be scanned assuming the sources follow standard java-ish hierarchy. That is a.b.c either maps to a directory `a/b/c` or a class file `a/b/c.java`.  |
+| `suspend_fun_to_async` | True/False | Converting Kotlin's suspend functions to Dart's async functions. Defaults to False.  |
 | `output:`              | (Subsection) | This subsection will contain configuration related to output files. |
 | `output:` >> `bindings_type` | `c_based` (default) or `dart_only` | Binding generation strategy. [Trade-offs](#pure-dart-bindings) are explained at the end of this document. |
 | `output:` >> `c:`      | (Subsection) | This subsection specified C output configuration. Required if `bindings_type` is `c_based`. |
diff --git a/pkgs/jnigen/example/README.md b/pkgs/jnigen/example/README.md
index a07a9d0..5b0bfe6 100644
--- a/pkgs/jnigen/example/README.md
+++ b/pkgs/jnigen/example/README.md
@@ -2,45 +2,50 @@
 
 This directory contains examples on how to use jnigen.
 
-| Directory | Description |
-| ------- | --------- |
-| [in_app_java](in_app_java/) | Demonstrates how to include custom Java code in Flutter application and call that using jnigen |
-| [pdfbox_plugin](pdfbox_plugin/) | Example of a flutter plugin which provides bindings to Apache PDFBox library. Currently works on Flutter desktop and Dart standalone on linux. |
-| [notification_plugin](notification_plugin/) | Example of a reusable Flutter plugin with custom Java code which uses Android libraries. |
+| Directory                                   | Description                                                                                                                                    |
+| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
+| [in_app_java](in_app_java/)                 | Demonstrates how to include custom Java code in Flutter application and call that using jnigen                                                 |
+| [pdfbox_plugin](pdfbox_plugin/)             | Example of a flutter plugin which provides bindings to Apache PDFBox library. Currently works on Flutter desktop and Dart standalone on linux. |
+| [notification_plugin](notification_plugin/) | Example of a reusable Flutter plugin with custom Java code which uses Android libraries.                                                       |
+| [kotlin_plugin](kotlin_plugin/)             | Example of using jnigen to generate bindings for Kotlin.                                                                                       |
 
 We intend to cover few more use cases in future.
 
 ## Creating a jnigen-based plugin from scratch
 
 ### Dart package (Standalone only)
-* Create dart package, add `jni` as dependency and `jnigen` as dev dependency.
-* Write the jnigen config similar to [the one in pdfbox_plugin](pdfbox_plugin/jnigen.yaml).
-* Generate JNI bindings by running `dart run jnigen --config jnigen.yaml`.
 
-* In the CLI project which uses this package, add this package, and `jni` as a dependency.
-* Run `dart run jni:setup` to build native libraries for JNI base library and jnigen generated package.
-* Import the package. See [pdf_info.dart](pdfbox_plugin/dart_example/bin/pdf_info.dart) for an example of using JNI from dart standalone.
+- Create dart package, add `jni` as dependency and `jnigen` as dev dependency.
+- Write the jnigen config similar to [the one in pdfbox_plugin](pdfbox_plugin/jnigen.yaml).
+- Generate JNI bindings by running `dart run jnigen --config jnigen.yaml`.
+
+- In the CLI project which uses this package, add this package, and `jni` as a dependency.
+- Run `dart run jni:setup` to build native libraries for JNI base library and jnigen generated package.
+- Import the package. See [pdf_info.dart](pdfbox_plugin/dart_example/bin/pdf_info.dart) for an example of using JNI from dart standalone.
 
 ### Flutter FFI plugin
+
 Flutter FFI plugin has the advantage of bundling the required native libraries along with Android / Linux Desktop app.
 
 To create an FFI plugin with JNI bindings:
 
-* Create a plugin using `plugin_ffi` template.
-* Remove ffigen-specific files and stubs.
-* Follow the above steps to generate JNI bindings. The plugin can be used from a flutter project.
+- Create a plugin using `plugin_ffi` template.
+- Remove ffigen-specific files and stubs.
+- Follow the above steps to generate JNI bindings. The plugin can be used from a flutter project.
 
-* It may be desirable to generate the bindings into a private directory (Eg: `lib/src/third_party`) and re-export the classes from the top level dart file.
+- It may be desirable to generate the bindings into a private directory (Eg: `lib/src/third_party`) and re-export the classes from the top level dart file.
 
-* To use the plugin from Dart projects as well, comment-out or remove flutter SDK requirements from the pubspec. This is however problematic if you want to publish the package.
+- To use the plugin from Dart projects as well, comment-out or remove flutter SDK requirements from the pubspec. This is however problematic if you want to publish the package.
 
 ### Android plugin with custom Java code
-* Create an FFI plugin with Android as the only platform.
-* Build the example/ Android project using command `flutter build apk`. After a release build is done, jnigen can use a gradle stub to collect compile classpaths.
-* Write your custom Java code in `android/src/main/java` hierarchy of the plugin.
-* Generate JNI bindings as described above. See [notification_plugin/jnigen.yaml](notification_plugin/jnigen.yaml) for example configuration.
+
+- Create an FFI plugin with Android as the only platform.
+- Build the example/ Android project using command `flutter build apk`. After a release build is done, jnigen can use a gradle stub to collect compile classpaths.
+- Write your custom Java code in `android/src/main/java` hierarchy of the plugin.
+- Generate JNI bindings as described above. See [notification_plugin/jnigen.yaml](notification_plugin/jnigen.yaml) for example configuration.
 
 ### Pure dart bindings
+
 With Pure dart bindings PoC, most of the FFI setup steps are not required. For example, a simple flutter package can be created instead of an FFI plugin, since there are no native artifacts to bundle.
 
 The generated bindings still depend on `package:jni`, therefore running `dart run jni:setup` is still a requirement on standalone target.
diff --git a/pkgs/jnigen/example/in_app_java/lib/android_utils.dart b/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
index 6929631..082b105 100644
--- a/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
+++ b/pkgs/jnigen/example/in_app_java/lib/android_utils.dart
@@ -12,6 +12,7 @@
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
 
+import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
diff --git a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h
index 12e38b9..39b70bd 100644
--- a/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h
+++ b/pkgs/jnigen/example/in_app_java/src/android_utils/dartjni.h
@@ -260,3 +260,13 @@
   if (exception == NULL) return NULL;
   return to_global_ref(exception);
 }
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result);
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j);
diff --git a/pkgs/jnigen/example/kotlin_plugin/.gitignore b/pkgs/jnigen/example/kotlin_plugin/.gitignore
new file mode 100644
index 0000000..96486fd
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/.gitignore
@@ -0,0 +1,30 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.packages
+build/
diff --git a/pkgs/jnigen/example/kotlin_plugin/.metadata b/pkgs/jnigen/example/kotlin_plugin/.metadata
new file mode 100644
index 0000000..2276419
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: 9b4416aaa79bf984d06aa40ab515a274a50a75a7
+  channel: beta
+
+project_type: package
diff --git a/pkgs/jnigen/example/kotlin_plugin/README.md b/pkgs/jnigen/example/kotlin_plugin/README.md
new file mode 100644
index 0000000..2a915c8
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/README.md
@@ -0,0 +1,38 @@
+# kotlin_plugin
+
+This example generates bindings for a Kotlin-based library. It showcases the conversion of `suspend fun` in Kotlin to `async` functions in Dart.
+
+The command to regenerate JNI bindings is:
+```
+flutter pub run jnigen --config jnigen.yaml # run from kotlin_plugin project root 
+```
+
+The `example/` app must be built at least once in _release_ mode (eg `flutter build apk`) before running jnigen. This is the equivalent of Gradle Sync in Android Studio, and enables `jnigen` to run a Gradle stub and determine release build's classpath, which contains the paths to relevant dependencies. Therefore a build must have been run after cleaning build directories, or updating Java dependencies. This is a known complexity of the Gradle build system, and if you know a solution, please contribute to issue discussion at #33.
+
+Note that `jnigen.yaml` of this example contains the option `suspend_fun_to_async: true`. This will generate `async` method bindings from Kotlin's `suspend fun`s.
+
+For Kotlin coroutines to work, `Jni.initDLApi()` must be run first.
+
+## Creating a new Kotlin plugin
+
+Running `flutter create --template=plugin_ffi --platform=android kotlin_plugin` creates the skeleton of an Android plugin.
+
+In order to use Kotlin, `build.gradle` must be modified in the following way:
+
+```gradle
+apply plugin: 'com.android.library'
+// Add the line below:
+apply plugin: 'kotlin-android'
+```
+
+```gradle
+android {
+    // Add the following block
+    sourceSets {
+        main.java.srcDirs += 'src/main/kotlin'
+    }
+    // ...
+}
+```
+
+Now the Kotlin code in `src/main/kotlin` is included in the plugin.
diff --git a/pkgs/jnigen/example/kotlin_plugin/analysis_options.yaml b/pkgs/jnigen/example/kotlin_plugin/analysis_options.yaml
new file mode 100644
index 0000000..f9b3034
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/analysis_options.yaml
@@ -0,0 +1 @@
+include: package:flutter_lints/flutter.yaml
diff --git a/pkgs/jnigen/example/kotlin_plugin/android/.gitignore b/pkgs/jnigen/example/kotlin_plugin/android/.gitignore
new file mode 100644
index 0000000..161bdcd
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/android/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx
diff --git a/pkgs/jnigen/example/kotlin_plugin/android/build.gradle b/pkgs/jnigen/example/kotlin_plugin/android/build.gradle
new file mode 100644
index 0000000..862afaf
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/android/build.gradle
@@ -0,0 +1,64 @@
+// The Android Gradle Plugin builds the native code with the Android NDK.
+
+group 'com.example.kotlin_plugin'
+version '1.0'
+
+buildscript {
+    repositories {
+        google()
+        mavenCentral()
+    }
+
+    dependencies {
+        // The Android Gradle Plugin knows how to build native code with the NDK.
+        classpath 'com.android.tools.build:gradle:7.1.2'
+    }
+}
+
+rootProject.allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+    // Bumping the plugin compileSdkVersion requires all clients of this plugin
+    // to bump the version in their app.
+    compileSdkVersion 31
+
+    // Bumping the plugin ndkVersion requires all clients of this plugin to bump
+    // the version in their app and to download a newer version of the NDK.
+    ndkVersion "21.1.6352462"
+
+    sourceSets {
+        main.java.srcDirs += 'src/main/kotlin'
+    }
+
+    // Invoke the shared CMake build with the Android Gradle Plugin.
+    externalNativeBuild {
+        cmake {
+            path "../src/CMakeLists.txt"
+
+            // The default CMake version for the Android Gradle Plugin is 3.10.2.
+            // https://developer.android.com/studio/projects/install-ndk#vanilla_cmake
+            //
+            // The Flutter tooling requires that developers have CMake 3.10 or later
+            // installed. You should not increase this version, as doing so will cause
+            // the plugin to fail to compile for some customers of the plugin.
+            // version "3.10.2"
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    defaultConfig {
+        minSdkVersion 16
+    }
+}
diff --git a/pkgs/jnigen/example/kotlin_plugin/android/settings.gradle b/pkgs/jnigen/example/kotlin_plugin/android/settings.gradle
new file mode 100644
index 0000000..8e0a5b1
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'notification_plugin'
diff --git a/pkgs/jnigen/example/kotlin_plugin/android/src/main/AndroidManifest.xml b/pkgs/jnigen/example/kotlin_plugin/android/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d7d511a
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/android/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="com.example.kotlin_plugin">
+</manifest>
diff --git a/pkgs/jnigen/example/kotlin_plugin/android/src/main/kotlin/Example.kt b/pkgs/jnigen/example/kotlin_plugin/android/src/main/kotlin/Example.kt
new file mode 100644
index 0000000..531f899
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/android/src/main/kotlin/Example.kt
@@ -0,0 +1,10 @@
+import androidx.annotation.Keep
+import kotlinx.coroutines.*
+
+@Keep
+class Example {
+  public suspend fun thinkBeforeAnswering(): String {
+    delay(1000L)
+    return "42"
+  }
+}
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/.gitignore b/pkgs/jnigen/example/kotlin_plugin/example/.gitignore
new file mode 100644
index 0000000..24476c5
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/.gitignore
@@ -0,0 +1,44 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/.metadata b/pkgs/jnigen/example/kotlin_plugin/example/.metadata
new file mode 100644
index 0000000..b510dd9
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/.metadata
@@ -0,0 +1,30 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled.
+
+version:
+  revision: 9b4416aaa79bf984d06aa40ab515a274a50a75a7
+  channel: beta
+
+project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+  platforms:
+    - platform: root
+      create_revision: 9b4416aaa79bf984d06aa40ab515a274a50a75a7
+      base_revision: 9b4416aaa79bf984d06aa40ab515a274a50a75a7
+    - platform: android
+      create_revision: 9b4416aaa79bf984d06aa40ab515a274a50a75a7
+      base_revision: 9b4416aaa79bf984d06aa40ab515a274a50a75a7
+
+  # User provided section
+
+  # List of Local paths (relative to this file) that should be
+  # ignored by the migrate tool.
+  #
+  # Files that are not part of the templates will be ignored by default.
+  unmanaged_files:
+    - 'lib/main.dart'
+    - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/README.md b/pkgs/jnigen/example/kotlin_plugin/example/README.md
new file mode 100644
index 0000000..2b3fce4
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/README.md
@@ -0,0 +1,16 @@
+# example
+
+A new Flutter project.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev/), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/analysis_options.yaml b/pkgs/jnigen/example/kotlin_plugin/example/analysis_options.yaml
new file mode 100644
index 0000000..61b6c4d
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/analysis_options.yaml
@@ -0,0 +1,29 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+  # The lint rules applied to this project can be customized in the
+  # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+  # included above or to enable additional rules. A list of all available lints
+  # and their documentation is published at
+  # https://dart-lang.github.io/linter/lints/index.html.
+  #
+  # Instead of disabling a lint rule for the entire project in the
+  # section below, it can also be suppressed for a single line of code
+  # or a specific dart file by using the `// ignore: name_of_lint` and
+  # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+  # producing the lint.
+  rules:
+    # avoid_print: false  # Uncomment to disable the `avoid_print` rule
+    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/.gitignore b/pkgs/jnigen/example/kotlin_plugin/example/android/.gitignore
new file mode 100644
index 0000000..6f56801
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/.gitignore
@@ -0,0 +1,13 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/build.gradle b/pkgs/jnigen/example/kotlin_plugin/example/android/app/build.gradle
new file mode 100644
index 0000000..f371e65
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/build.gradle
@@ -0,0 +1,71 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withReader('UTF-8') { reader ->
+        localProperties.load(reader)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+    flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+    flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+    compileSdkVersion flutter.compileSdkVersion
+    ndkVersion flutter.ndkVersion
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    sourceSets {
+        main.java.srcDirs += 'src/main/kotlin'
+    }
+
+    defaultConfig {
+        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+        applicationId "com.example.kotlin_plugin.example"
+        // You can update the following values to match your application needs.
+        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
+        minSdkVersion flutter.minSdkVersion
+        targetSdkVersion flutter.targetSdkVersion
+        versionCode flutterVersionCode.toInteger()
+        versionName flutterVersionName
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig signingConfigs.debug
+        }
+    }
+}
+
+flutter {
+    source '../..'
+}
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/debug/AndroidManifest.xml b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000..0d1fc86
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.kotlin_plugin.example">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/AndroidManifest.xml b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..9059f10
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.kotlin_plugin.example">
+   <application
+        android:label="example"
+        android:name="${applicationName}"
+        android:icon="@mipmap/ic_launcher">
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:launchMode="singleTop"
+            android:theme="@style/LaunchTheme"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+            android:hardwareAccelerated="true"
+            android:windowSoftInputMode="adjustResize">
+            <!-- Specifies an Android theme to apply to this Activity as soon as
+                 the Android process has started. This theme is visible to the user
+                 while the Flutter UI initializes. After that, this theme continues
+                 to determine the Window background behind the Flutter UI. -->
+            <meta-data
+              android:name="io.flutter.embedding.android.NormalTheme"
+              android:resource="@style/NormalTheme"
+              />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <!-- Don't delete the meta-data below.
+             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+        <meta-data
+            android:name="flutterEmbedding"
+            android:value="2" />
+    </application>
+</manifest>
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/kotlin/com/example/kotlin_plugin/example/MainActivity.kt b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/kotlin/com/example/kotlin_plugin/example/MainActivity.kt
new file mode 100644
index 0000000..4826f82
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/kotlin/com/example/kotlin_plugin/example/MainActivity.kt
@@ -0,0 +1,6 @@
+package com.example.kotlin_plugin.example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/drawable-v21/launch_background.xml b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000..f74085f
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="?android:colorBackground" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/drawable/launch_background.xml b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000..304732f
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:color/white" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..db77bb4
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..17987b7
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..09d4391
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..d5f1c8d
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..4d6372e
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/values-night/styles.xml b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000..06952be
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/values/styles.xml b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..cb1ef88
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             the Flutter engine draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+    <!-- Theme applied to the Android Window as soon as the process has started.
+         This theme determines the color of the Android Window while your
+         Flutter UI initializes, as well as behind your Flutter UI while its
+         running.
+
+         This Theme is only used starting with V2 of Flutter's Android embedding. -->
+    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+        <item name="android:windowBackground">?android:colorBackground</item>
+    </style>
+</resources>
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/profile/AndroidManifest.xml b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000..2c06d9b
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.datastore.example">
+    <!-- The INTERNET permission is required for development. Specifically,
+         the Flutter tool needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/build.gradle b/pkgs/jnigen/example/kotlin_plugin/example/android/build.gradle
new file mode 100644
index 0000000..58a8c74
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+    ext.kotlin_version = '1.7.10'
+    repositories {
+        google()
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:7.2.0'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        mavenCentral()
+    }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+    project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/gradle.properties b/pkgs/jnigen/example/kotlin_plugin/example/android/gradle.properties
new file mode 100644
index 0000000..94adc3a
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/gradle/wrapper/gradle-wrapper.properties b/pkgs/jnigen/example/kotlin_plugin/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..3c472b9
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/android/settings.gradle b/pkgs/jnigen/example/kotlin_plugin/example/android/settings.gradle
new file mode 100644
index 0000000..44e62bc
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/lib/main.dart b/pkgs/jnigen/example/kotlin_plugin/example/lib/main.dart
new file mode 100644
index 0000000..df87f6e
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/lib/main.dart
@@ -0,0 +1,98 @@
+import 'package:flutter/material.dart';
+import 'package:kotlin_plugin/kotlin_plugin.dart';
+import 'package:jni/jni.dart';
+
+void main() {
+  Jni.initDLApi();
+  runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+  const MyApp({super.key});
+
+  @override
+  Widget build(BuildContext context) {
+    return MaterialApp(
+      title: 'Kotlin Plugin Example',
+      theme: ThemeData(
+        primarySwatch: Colors.blue,
+      ),
+      home: const MyHomePage(title: 'Kotlin Plugin Example Home Page'),
+    );
+  }
+}
+
+class MyHomePage extends StatefulWidget {
+  const MyHomePage({super.key, required this.title});
+
+  final String title;
+
+  @override
+  State<MyHomePage> createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State<MyHomePage> {
+  Future<String>? answer;
+
+  late Example example;
+
+  @override
+  void initState() {
+    super.initState();
+    example = Example();
+  }
+
+  @override
+  void dispose() {
+    example.delete();
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(widget.title),
+      ),
+      body: Center(
+        child: Column(
+          mainAxisAlignment: MainAxisAlignment.center,
+          children: <Widget>[
+            const Text(
+              'What is the answer to life, the universe, and everything?',
+            ),
+            ElevatedButton(
+              onPressed: () {
+                setState(() {
+                  answer = example.thinkBeforeAnswering().then(
+                      (value) => value.toDartString(deleteOriginal: true));
+                });
+              },
+              child: const Text('Think...'),
+            ),
+            FutureBuilder<String>(
+              future: answer,
+              builder: (context, snapshot) {
+                switch (snapshot.connectionState) {
+                  case ConnectionState.none:
+                    return const SizedBox();
+                  case ConnectionState.waiting:
+                  case ConnectionState.active:
+                    return Text(
+                      'Thinking...',
+                      style: Theme.of(context).textTheme.headlineMedium,
+                    );
+                  case ConnectionState.done:
+                    return Text(
+                      snapshot.data ?? "I don't know!",
+                      style: Theme.of(context).textTheme.headlineMedium,
+                    );
+                }
+              },
+            ),
+          ],
+        ),
+      ),
+    );
+  }
+}
diff --git a/pkgs/jnigen/example/kotlin_plugin/example/pubspec.yaml b/pkgs/jnigen/example/kotlin_plugin/example/pubspec.yaml
new file mode 100644
index 0000000..4292ba9
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/example/pubspec.yaml
@@ -0,0 +1,29 @@
+name: example
+description: A new Flutter project.
+
+publish_to: "none"
+
+version: 1.0.0+1
+
+environment:
+  sdk: ">=2.19.0-444.3.beta <3.0.0"
+
+dependencies:
+  flutter:
+    sdk: flutter
+
+  kotlin_plugin:
+    path: ../
+  
+  jni:
+    path: ../../../../jni
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+
+  flutter_lints: ^2.0.0
+
+flutter:
+  uses-material-design: true
+
diff --git a/pkgs/jnigen/example/kotlin_plugin/jnigen.yaml b/pkgs/jnigen/example/kotlin_plugin/jnigen.yaml
new file mode 100644
index 0000000..60978f5
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/jnigen.yaml
@@ -0,0 +1,21 @@
+android_sdk_config:
+  add_gradle_deps: true
+  android_example: 'example/'
+
+summarizer:
+  backend: asm
+
+suspend_fun_to_async: true
+
+output:
+  c:
+    library_name: kotlin_plugin_bindings
+    path: src/
+  dart:
+    path: lib/kotlin_bindings.dart
+    structure: single_file
+
+log_level: all
+
+classes:
+  - 'Example'
diff --git a/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart b/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart
new file mode 100644
index 0000000..e5c8331
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_bindings.dart
@@ -0,0 +1,87 @@
+// Autogenerated by jnigen. DO NOT EDIT!
+
+// ignore_for_file: annotate_overrides
+// ignore_for_file: camel_case_extensions
+// ignore_for_file: camel_case_types
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: file_names
+// ignore_for_file: no_leading_underscores_for_local_identifiers
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
+// ignore_for_file: unnecessary_cast
+// ignore_for_file: unused_element
+// ignore_for_file: unused_import
+
+import "dart:isolate" show ReceivePort;
+import "dart:ffi" as ffi;
+import "package:jni/internal_helpers_for_jnigen.dart";
+import "package:jni/jni.dart" as jni;
+
+// Auto-generated initialization code.
+
+final ffi.Pointer<T> Function<T extends ffi.NativeType>(String sym) jniLookup =
+    ProtectedJniExtensions.initGeneratedLibrary("kotlin_plugin_bindings");
+
+/// from: Example
+class Example extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  Example.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static const type = $ExampleType();
+
+  static final _ctor =
+      jniLookup<ffi.NativeFunction<jni.JniResult Function()>>("Example__ctor")
+          .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  Example() : super.fromRef(_ctor().object);
+
+  static final _thinkBeforeAnswering = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("Example__thinkBeforeAnswering")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public final java.lang.Object thinkBeforeAnswering(kotlin.coroutines.Continuation continuation)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  Future<jni.JString> thinkBeforeAnswering() async {
+    final $p = ReceivePort();
+    final $c = jni.Jni.newPortContinuation($p);
+    _thinkBeforeAnswering(reference, $c).object;
+    final $o = jni.JObjectPtr.fromAddress(await $p.first);
+    final $k = const jni.JStringType().getClass().reference;
+    if (jni.Jni.env.IsInstanceOf($o, $k) == 0) {
+      throw "Failed";
+    }
+    return const jni.JStringType().fromRef($o);
+  }
+}
+
+class $ExampleType extends jni.JObjType<Example> {
+  const $ExampleType();
+
+  @override
+  String get signature => r"LExample;";
+
+  @override
+  Example fromRef(jni.JObjectPtr ref) => Example.fromRef(ref);
+}
+
+extension $ExampleArray on jni.JArray<Example> {
+  Example operator [](int index) {
+    return (elementType as $ExampleType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, Example value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
diff --git a/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_plugin.dart b/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_plugin.dart
new file mode 100644
index 0000000..5919c22
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/lib/kotlin_plugin.dart
@@ -0,0 +1,3 @@
+library kotlin_plugin;
+
+export 'kotlin_bindings.dart';
diff --git a/pkgs/jnigen/example/kotlin_plugin/pubspec.yaml b/pkgs/jnigen/example/kotlin_plugin/pubspec.yaml
new file mode 100644
index 0000000..f6b5916
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/pubspec.yaml
@@ -0,0 +1,31 @@
+# Copyright (c) 2023, the Dart project authors.  Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+name: kotlin_plugin
+description: Example of using jnigen to generate bindings for Kotlin.
+version: 0.0.1
+publish_to: none
+
+environment:
+  sdk: '>=2.19.0-444.3.beta <3.0.0'
+  flutter: ">=1.17.0"
+
+dependencies:
+  flutter:
+    sdk: flutter
+  jni:
+    path: ../../../jni
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+  flutter_lints: ^2.0.0
+  jnigen:
+    path: ../../
+
+flutter:
+  plugin:
+    platforms:
+      android:
+        ffiPlugin: true
diff --git a/pkgs/jnigen/example/kotlin_plugin/src/.clang-format b/pkgs/jnigen/example/kotlin_plugin/src/.clang-format
new file mode 100644
index 0000000..a256c2f
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/src/.clang-format
@@ -0,0 +1,15 @@
+# From dart SDK: https://github.com/dart-lang/sdk/blob/main/.clang-format
+
+# Defines the Chromium style for automatic reformatting.
+# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
+BasedOnStyle: Chromium
+
+# clang-format doesn't seem to do a good job of this for longer comments.
+ReflowComments: 'false'
+
+# We have lots of these. Though we need to put them all in curly braces,
+# clang-format can't do that.
+AllowShortIfStatementsOnASingleLine: 'true'
+
+# Put escaped newlines into the rightmost column.
+AlignEscapedNewlinesLeft: false
diff --git a/pkgs/jnigen/example/kotlin_plugin/src/CMakeLists.txt b/pkgs/jnigen/example/kotlin_plugin/src/CMakeLists.txt
new file mode 100644
index 0000000..78d9dca
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/src/CMakeLists.txt
@@ -0,0 +1,32 @@
+# jni_native_build (Build with jni:setup. Do not delete this line.)
+
+# The Flutter tooling requires that developers have CMake 3.10 or later
+# installed. You should not increase this version, as doing so will cause
+# the plugin to fail to compile for some customers of the plugin.
+cmake_minimum_required(VERSION 3.10)
+
+project(kotlin_plugin_bindings VERSION 0.0.1 LANGUAGES C)
+
+add_library(kotlin_plugin_bindings SHARED
+  "./kotlin_plugin_bindings.c"
+)
+
+set_target_properties(kotlin_plugin_bindings PROPERTIES
+  OUTPUT_NAME "kotlin_plugin_bindings"
+)
+
+target_compile_definitions(kotlin_plugin_bindings PUBLIC DART_SHARED_LIB)
+
+if(WIN32)
+	set_target_properties(${TARGET_NAME} PROPERTIES
+		LINK_FLAGS "/DELAYLOAD:jvm.dll")
+endif()
+
+if (ANDROID)
+	target_link_libraries(kotlin_plugin_bindings log)
+else()
+	find_package(Java REQUIRED)
+	find_package(JNI REQUIRED)
+	include_directories(${JNI_INCLUDE_DIRS})
+	target_link_libraries(kotlin_plugin_bindings ${JNI_LIBRARIES})
+endif()
diff --git a/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h
new file mode 100644
index 0000000..39b70bd
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/src/dartjni.h
@@ -0,0 +1,272 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#pragma once
+
+// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h.
+#include <jni.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if _WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+#if _WIN32
+#define FFI_PLUGIN_EXPORT __declspec(dllexport)
+#else
+#define FFI_PLUGIN_EXPORT
+#endif
+
+#if defined _WIN32
+#define thread_local __declspec(thread)
+#else
+#define thread_local __thread
+#endif
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
+#ifdef __ANDROID__
+#define __ENVP_CAST (JNIEnv**)
+#else
+#define __ENVP_CAST (void**)
+#endif
+
+/// Stores the global state of the JNI.
+typedef struct JniContext {
+  JavaVM* jvm;
+  jobject classLoader;
+  jmethodID loadClassMethod;
+  jobject currentActivity;
+  jobject appContext;
+} JniContext;
+
+// jniEnv for this thread, used by inline functions in this header,
+// therefore declared as extern.
+extern thread_local JNIEnv* jniEnv;
+
+extern JniContext jni;
+
+/// Types used by JNI API to distinguish between primitive types.
+enum JniType {
+  booleanType = 0,
+  byteType = 1,
+  shortType = 2,
+  charType = 3,
+  intType = 4,
+  longType = 5,
+  floatType = 6,
+  doubleType = 7,
+  objectType = 8,
+  voidType = 9,
+};
+
+/// Result type for use by JNI.
+///
+/// If [exception] is null, it means the result is valid.
+/// It's assumed that the caller knows the expected type in [result].
+typedef struct JniResult {
+  jvalue result;
+  jthrowable exception;
+} JniResult;
+
+/// Similar to [JniResult] but for class lookups.
+typedef struct JniClassLookupResult {
+  jclass classRef;
+  jthrowable exception;
+} JniClassLookupResult;
+
+/// Similar to [JniResult] but for method/field ID lookups.
+typedef struct JniPointerResult {
+  void* id;
+  jthrowable exception;
+} JniPointerResult;
+
+/// JniExceptionDetails holds 2 jstring objects, one is the result of
+/// calling `toString` on exception object, other is stack trace;
+typedef struct JniExceptionDetails {
+  jstring message;
+  jstring stacktrace;
+} JniExceptionDetails;
+
+/// This struct contains functions which wrap method call / field access conveniently along with
+/// exception checking.
+///
+/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us
+/// to check for and clear the exception before returning to dart code, which requires these functions
+/// to return result types.
+typedef struct JniAccessors {
+  JniClassLookupResult (*getClass)(char* internalName);
+  JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature);
+  JniPointerResult (*getStaticFieldID)(jclass cls,
+                                       char* fieldName,
+                                       char* signature);
+  JniPointerResult (*getMethodID)(jclass cls,
+                                  char* methodName,
+                                  char* signature);
+  JniPointerResult (*getStaticMethodID)(jclass cls,
+                                        char* methodName,
+                                        char* signature);
+  JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args);
+  JniPointerResult (*newPrimitiveArray)(jsize length, int type);
+  JniPointerResult (*newObjectArray)(jsize length,
+                                     jclass elementClass,
+                                     jobject initialElement);
+  JniResult (*getArrayElement)(jarray array, int index, int type);
+  JniResult (*callMethod)(jobject obj,
+                          jmethodID methodID,
+                          int callType,
+                          jvalue* args);
+  JniResult (*callStaticMethod)(jclass cls,
+                                jmethodID methodID,
+                                int callType,
+                                jvalue* args);
+  JniResult (*getField)(jobject obj, jfieldID fieldID, int callType);
+  JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType);
+  JniExceptionDetails (*getExceptionDetails)(jthrowable exception);
+} JniAccessors;
+
+FFI_PLUGIN_EXPORT JniAccessors* GetAccessors();
+
+FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void);
+
+FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void);
+
+FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args);
+
+FFI_PLUGIN_EXPORT jclass LoadClass(const char* name);
+
+FFI_PLUGIN_EXPORT jobject GetClassLoader(void);
+
+FFI_PLUGIN_EXPORT jobject GetApplicationContext(void);
+
+FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void);
+
+// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c
+// once migration to pure dart bindings is complete.
+
+// `static inline` because `inline` doesn't work, it may still not
+// inline the function in which case a linker error may be produced.
+//
+// There has to be a better way to do this. Either to force inlining on target
+// platforms, or just leave it as normal function.
+
+static inline void __load_class_into(jclass* cls, const char* name) {
+#ifdef __ANDROID__
+  jstring className = (*jniEnv)->NewStringUTF(jniEnv, name);
+  *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader,
+                                     jni.loadClassMethod, className);
+  (*jniEnv)->DeleteLocalRef(jniEnv, className);
+#else
+  *cls = (*jniEnv)->FindClass(jniEnv, name);
+#endif
+}
+
+static inline void load_class(jclass* cls, const char* name) {
+  if (*cls == NULL) {
+    __load_class_into(cls, name);
+  }
+}
+
+static inline void load_class_gr(jclass* cls, const char* name) {
+  if (*cls == NULL) {
+    jclass tmp;
+    __load_class_into(&tmp, name);
+    *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp);
+    (*jniEnv)->DeleteLocalRef(jniEnv, tmp);
+  }
+}
+
+static inline void attach_thread() {
+  if (jniEnv == NULL) {
+    (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL);
+  }
+}
+
+static inline void load_method(jclass cls,
+                               jmethodID* res,
+                               const char* name,
+                               const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline void load_static_method(jclass cls,
+                                      jmethodID* res,
+                                      const char* name,
+                                      const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline void load_field(jclass cls,
+                              jfieldID* res,
+                              const char* name,
+                              const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline void load_static_field(jclass cls,
+                                     jfieldID* res,
+                                     const char* name,
+                                     const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline jobject to_global_ref(jobject ref) {
+  jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref);
+  (*jniEnv)->DeleteLocalRef(jniEnv, ref);
+  return g;
+}
+
+// These functions are useful for C+Dart bindings, and not required for pure dart bindings.
+
+FFI_PLUGIN_EXPORT JniContext GetJniContext();
+/// For use by jni_gen's generated code
+/// don't use these.
+
+// these 2 fn ptr vars will be defined by generated code library
+extern JniContext (*context_getter)(void);
+extern JNIEnv* (*env_getter)(void);
+
+// this function will be exported by generated code library
+// it will set above 2 variables.
+FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void),
+                                     JNIEnv* (*eg)(void));
+
+static inline void load_env() {
+  if (jniEnv == NULL) {
+    jni = context_getter();
+    jniEnv = env_getter();
+  }
+}
+
+static inline jthrowable check_exception() {
+  jthrowable exception = (*jniEnv)->ExceptionOccurred(jniEnv);
+  if (exception != NULL) (*jniEnv)->ExceptionClear(jniEnv);
+  if (exception == NULL) return NULL;
+  return to_global_ref(exception);
+}
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result);
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j);
diff --git a/pkgs/jnigen/example/kotlin_plugin/src/kotlin_plugin_bindings.c b/pkgs/jnigen/example/kotlin_plugin/src/kotlin_plugin_bindings.c
new file mode 100644
index 0000000..77075ce
--- /dev/null
+++ b/pkgs/jnigen/example/kotlin_plugin/src/kotlin_plugin_bindings.c
@@ -0,0 +1,52 @@
+// Autogenerated by jnigen. DO NOT EDIT!
+
+#include <stdint.h>
+#include "dartjni.h"
+#include "jni.h"
+
+thread_local JNIEnv* jniEnv;
+JniContext jni;
+
+JniContext (*context_getter)(void);
+JNIEnv* (*env_getter)(void);
+
+void setJniGetters(JniContext (*cg)(void), JNIEnv* (*eg)(void)) {
+  context_getter = cg;
+  env_getter = eg;
+}
+
+// Example
+jclass _c_Example = NULL;
+
+jmethodID _m_Example__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__ctor() {
+  load_env();
+  load_class_gr(&_c_Example, "Example");
+  if (_c_Example == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__ctor, "<init>", "()V");
+  if (_m_Example__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->NewObject(jniEnv, _c_Example, _m_Example__ctor);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_Example__thinkBeforeAnswering = NULL;
+FFI_PLUGIN_EXPORT
+JniResult Example__thinkBeforeAnswering(jobject self_, jobject continuation) {
+  load_env();
+  load_class_gr(&_c_Example, "Example");
+  if (_c_Example == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_Example, &_m_Example__thinkBeforeAnswering,
+              "thinkBeforeAnswering",
+              "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;");
+  if (_m_Example__thinkBeforeAnswering == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallObjectMethod(
+      jniEnv, self_, _m_Example__thinkBeforeAnswering, continuation);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
diff --git a/pkgs/jnigen/example/notification_plugin/README.md b/pkgs/jnigen/example/notification_plugin/README.md
index 035b29a..e1b3af5 100644
--- a/pkgs/jnigen/example/notification_plugin/README.md
+++ b/pkgs/jnigen/example/notification_plugin/README.md
@@ -1,12 +1,12 @@
 # notification_plugin
 
-Example of android plugin project with jnigen.
+Example of Android plugin project with jnigen.
 
 This plugin project contains [custom code](android/src/main/java/com/example/notification_plugin) which uses the Android libraries. The bindings are generated using [jnigen config](jnigen.yaml) and then used in [flutter example](example/lib/main.dart), with help of `package:jni` APIs.
 
 The command to regenerate JNI bindings is:
 ```
-flutter run jnigen --config jnigen.yaml # run from notification_plugin project root 
+flutter pub run jnigen --config jnigen.yaml # run from notification_plugin project root 
 ```
 
 The `example/` app must be built at least once in _release_ mode (eg `flutter build apk`) before running jnigen. This is the equivalent of Gradle Sync in Android Studio, and enables `jnigen` to run a Gradle stub and determine release build's classpath, which contains the paths to relevant dependencies. Therefore a build must have been run after cleaning build directories, or updating Java dependencies. This is a known complexity of the Gradle build system, and if you know a solution, please contribute to issue discussion at #33.
diff --git a/pkgs/jnigen/example/notification_plugin/lib/notifications.dart b/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
index a8ccd10..853f2a9 100644
--- a/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
+++ b/pkgs/jnigen/example/notification_plugin/lib/notifications.dart
@@ -16,6 +16,7 @@
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
 
+import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
diff --git a/pkgs/jnigen/example/notification_plugin/src/dartjni.h b/pkgs/jnigen/example/notification_plugin/src/dartjni.h
index 12e38b9..39b70bd 100644
--- a/pkgs/jnigen/example/notification_plugin/src/dartjni.h
+++ b/pkgs/jnigen/example/notification_plugin/src/dartjni.h
@@ -260,3 +260,13 @@
   if (exception == NULL) return NULL;
   return to_global_ref(exception);
 }
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result);
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j);
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
index f32c119..3ff6a53 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocument.dart
@@ -30,6 +30,7 @@
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
 
+import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
index 46928c0..0ea6ad6 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/pdmodel/PDDocumentInformation.dart
@@ -30,6 +30,7 @@
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
 
+import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
diff --git a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
index e442778..2094a0b 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
+++ b/pkgs/jnigen/example/pdfbox_plugin/lib/src/third_party/org/apache/pdfbox/text/PDFTextStripper.dart
@@ -30,6 +30,7 @@
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
 
+import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
diff --git a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h
index 12e38b9..39b70bd 100644
--- a/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h
+++ b/pkgs/jnigen/example/pdfbox_plugin/src/third_party/dartjni.h
@@ -260,3 +260,13 @@
   if (exception == NULL) return NULL;
   return to_global_ref(exception);
 }
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result);
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j);
diff --git a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java
index dc0f5a4..3c52c10 100644
--- a/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java
+++ b/pkgs/jnigen/java/src/main/java/com/github/dart_lang/jnigen/apisummarizer/disasm/AsmTypeUsageSignatureVisitor.java
@@ -78,7 +78,7 @@
     typeUsage.shorthand = name.substring(0, name.length()).replace('/', '.');
     var components = name.split("[/$]");
     var simpleName = components[components.length - 1];
-    typeUsage.type = new TypeUsage.DeclaredType(name, simpleName, new ArrayList<>());
+    typeUsage.type = new TypeUsage.DeclaredType(typeUsage.shorthand, simpleName, new ArrayList<>());
   }
 
   @Override
diff --git a/pkgs/jnigen/lib/src/bindings/c_bindings.dart b/pkgs/jnigen/lib/src/bindings/c_bindings.dart
index 8a989d3..488e3d8 100644
--- a/pkgs/jnigen/lib/src/bindings/c_bindings.dart
+++ b/pkgs/jnigen/lib/src/bindings/c_bindings.dart
@@ -35,9 +35,11 @@
   String generateBinding(ClassDecl c) => _class(c);
 
   String _class(ClassDecl c) {
+    if (!c.isIncluded) {
+      return '';
+    }
     final s = StringBuffer();
     final classNameInC = getUniqueClassName(c);
-
     // global variable in C that holds the reference to class
     final classVar = '${classVarPrefix}_$classNameInC';
     s.write('// ${c.binaryName}\n'
diff --git a/pkgs/jnigen/lib/src/bindings/common.dart b/pkgs/jnigen/lib/src/bindings/common.dart
index 19fba11..480a038 100644
--- a/pkgs/jnigen/lib/src/bindings/common.dart
+++ b/pkgs/jnigen/lib/src/bindings/common.dart
@@ -97,6 +97,10 @@
       args.add(
           '${getDartOuterType(param.type, resolver)} ${kwRename(param.name)}');
     }
+    if (m.asyncReturnType != null) {
+      // Remove the continuation object in async methods.
+      args.removeLast();
+    }
     return args.join(', ');
   }
 
@@ -107,6 +111,10 @@
       final paramName = kwRename(param.name);
       args.add(toNativeArg(paramName, param.type));
     }
+    if (m.asyncReturnType != null) {
+      // Change the continuation object in async methods.
+      args.last = '\$c';
+    }
     return args.join(', ');
   }
 
@@ -336,8 +344,17 @@
               type.params.map((param) => _dartType(param, resolver: resolver));
 
           // Replacing the declared ones. They come at the end.
+          // The rest will be JObject.
           if (allTypeParams.length >= type.params.length) {
             allTypeParams.replaceRange(
+              0,
+              allTypeParams.length - type.params.length,
+              List.filled(
+                allTypeParams.length - type.params.length,
+                jniObjectType,
+              ),
+            );
+            allTypeParams.replaceRange(
               allTypeParams.length - type.params.length,
               allTypeParams.length,
               paramTypeClasses,
@@ -425,8 +442,17 @@
             (param) => _getDartTypeClass(param, resolver, addConst: false));
 
         // Replacing the declared ones. They come at the end.
+        // The rest will be JObject.
         if (allTypeParams.length >= type.params.length) {
           allTypeParams.replaceRange(
+            0,
+            allTypeParams.length - type.params.length,
+            List.filled(
+              allTypeParams.length - type.params.length,
+              'const $jniObjectTypeClass()',
+            ),
+          );
+          allTypeParams.replaceRange(
             allTypeParams.length - type.params.length,
             allTypeParams.length,
             paramTypeClasses.map((param) => param.name),
@@ -690,6 +716,10 @@
   }
 }
 
+String toFuture(String type) {
+  return 'Future<$type>';
+}
+
 bool isPrimitive(TypeUsage t) => t.kind == Kind.primitive;
 bool isVoid(TypeUsage t) => isPrimitive(t) && t.name == 'void';
 
diff --git a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart b/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
index e9d58c0..d1ad576 100644
--- a/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
+++ b/pkgs/jnigen/lib/src/bindings/dart_bindings.dart
@@ -21,7 +21,7 @@
 
   static final indent = ' ' * 2;
 
-  static const voidPointer = BindingsGenerator.voidPointer;
+  static const voidPointer = BindingsGenerator.jobjectType;
 
   static const ffiVoidType = BindingsGenerator.ffiVoidType;
 
@@ -91,8 +91,15 @@
     final sym = '_$name';
     final ffiSig = dartSigForMethod(m, isFfiSig: true);
     final dartSig = dartSigForMethod(m, isFfiSig: false);
-    final returnType = getDartOuterType(m.returnType, resolver);
-    final returnTypeClass = getDartTypeClass(m.returnType, resolver);
+    var returnType = getDartOuterType(
+      m.asyncReturnType ?? m.returnType,
+      resolver,
+    );
+    if (m.asyncReturnType != null) {
+      returnType = toFuture(returnType);
+    }
+    final returnTypeClass =
+        getDartTypeClass(m.asyncReturnType ?? m.returnType, resolver);
     final ifStaticMethod = isStaticMethod(m) ? 'static' : '';
 
     // Load corresponding C method.
@@ -121,13 +128,34 @@
 
     final resultGetter = getJValueAccessor(m.returnType);
     var wrapperExpr = '$sym(${actualArgs(m)}).$resultGetter';
-    wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
     final typeParamsWithExtend =
         dartTypeParams(m.typeParams, includeExtends: true);
     final params = getFormalArgs(c, m, resolver);
     s.write(
-        '$indent$ifStaticMethod $returnType $name$typeParamsWithExtend($params) '
-        '=> $wrapperExpr;\n');
+      '$indent$ifStaticMethod $returnType $name$typeParamsWithExtend($params) ',
+    );
+    if (m.asyncReturnType == null) {
+      wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
+      s.write('=> $wrapperExpr;\n');
+    } else {
+      s.write(
+        'async {\n'
+        '${indent * 2}final \$p = ReceivePort();\n'
+        '${indent * 2}final \$c = ${jni}Jni.newPortContinuation(\$p);\n'
+        '${indent * 2}$wrapperExpr;\n'
+        '${indent * 2}final \$o = $voidPointer.fromAddress(await \$p.first);\n'
+        '${indent * 2}final \$k = $returnTypeClass.getClass().reference;\n'
+        '${indent * 2}if (${jni}Jni.env.IsInstanceOf(\$o, \$k) == 0) {\n'
+        '${indent * 3}throw "Failed";\n'
+        '${indent * 2}}\n'
+        '${indent * 2}return ${toDartResult(
+          '\$o',
+          m.returnType,
+          returnTypeClass,
+        )};\n'
+        '}\n',
+      );
+    }
     return s.toString();
   }
 
@@ -203,7 +231,8 @@
       '\n\n';
   static const autoGeneratedNotice = '// Autogenerated by jnigen. '
       'DO NOT EDIT!\n\n';
-  static const defaultImports = 'import "dart:ffi" as ffi;\n'
+  static const defaultImports = 'import "dart:isolate" show ReceivePort;\n'
+      'import "dart:ffi" as ffi;\n'
       'import "package:jni/internal_helpers_for_jnigen.dart";\n'
       'import "package:jni/jni.dart" as jni;\n\n';
   static const defaultLintSuppressions =
diff --git a/pkgs/jnigen/lib/src/bindings/preprocessor.dart b/pkgs/jnigen/lib/src/bindings/preprocessor.dart
index acc6f2b..0003f22 100644
--- a/pkgs/jnigen/lib/src/bindings/preprocessor.dart
+++ b/pkgs/jnigen/lib/src/bindings/preprocessor.dart
@@ -11,6 +11,8 @@
 
 /// Preprocessor which fills information needed by both Dart and C generators.
 abstract class ApiPreprocessor {
+  static const kotlinContinutationType = 'kotlin.coroutines.Continuation';
+
   static void preprocessAll(Map<String, ClassDecl> classes, Config config,
       {bool renameClasses = false}) {
     final classNameCounts = <String, int>{};
@@ -34,10 +36,11 @@
   static void _preprocess(
       ClassDecl decl, Map<String, ClassDecl> classes, Config config) {
     if (decl.isPreprocessed) return;
+
     if (!_isClassIncluded(decl, config)) {
       decl.isIncluded = false;
-      log.fine('exclude class ${decl.binaryName}');
       decl.isPreprocessed = true;
+      log.fine('exclude class ${decl.binaryName}');
       // Excluding all the class's methods and fields
       for (final method in decl.methods) {
         method.isIncluded = false;
@@ -78,6 +81,18 @@
         log.fine('exclude method ${decl.binaryName}#${method.name}');
         continue;
       }
+      if (config.suspendFunToAsync &&
+          method.params.isNotEmpty &&
+          method.params.last.type.kind == Kind.declared &&
+          method.params.last.type.shorthand == kotlinContinutationType) {
+        final continuationType = method.params.last.type.type as DeclaredType;
+        method.asyncReturnType = continuationType.params.isEmpty
+            ? TypeUsage.object
+            : continuationType.params.first;
+      } else {
+        method.asyncReturnType = null;
+      }
+
       var realName = method.name;
       if (isCtor(method)) {
         realName = 'ctor';
diff --git a/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart b/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
index 46f168b..67ceeed 100644
--- a/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
+++ b/pkgs/jnigen/lib/src/bindings/pure_dart_bindings.dart
@@ -23,10 +23,9 @@
   static const ffi = BindingsGenerator.ffi;
   static const jni = BindingsGenerator.jni;
 
-  static const voidPointer = BindingsGenerator.voidPointer;
+  static const voidPointer = BindingsGenerator.jobjectType;
   static const ffiVoidType = BindingsGenerator.ffiVoidType;
   static const jniObjectType = BindingsGenerator.jniObjectType;
-  static const jobjectType = BindingsGenerator.jobjectType;
 
   static final _deleteInstruction = BindingsGenerator.deleteInstruction;
 
@@ -117,8 +116,15 @@
 
     // Different logic for constructor and method;
     // For constructor, we want return type to be new object.
-    final returnType = getDartOuterType(m.returnType, resolver);
-    final returnTypeClass = getDartTypeClass(m.returnType, resolver);
+    var returnType = getDartOuterType(
+      m.asyncReturnType ?? m.returnType,
+      resolver,
+    );
+    if (m.asyncReturnType != null) {
+      returnType = toFuture(returnType);
+    }
+    final returnTypeClass =
+        getDartTypeClass(m.asyncReturnType ?? m.returnType, resolver);
     s.write('$indent/// from: ${getOriginalMethodHeader(m)}\n');
     if (!isPrimitive(m.returnType) || isCtor(m)) {
       s.write(_deleteInstruction);
@@ -148,9 +154,31 @@
     var wrapperExpr = '$accessors.call${ifStatic}MethodWithArgs'
         '($selfArgument, $mID, $callType, [${actualArgs(m)}])'
         '.$resultGetter';
-    wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
     s.write(
-        '$returnType $name$typeParamsWithExtend(${getFormalArgs(c, m, resolver)}) => $wrapperExpr;\n');
+      '$returnType $name$typeParamsWithExtend(${getFormalArgs(c, m, resolver)}) ',
+    );
+    if (m.asyncReturnType == null) {
+      wrapperExpr = toDartResult(wrapperExpr, m.returnType, returnTypeClass);
+      s.write('=> $wrapperExpr;\n');
+    } else {
+      s.write(
+        'async {\n'
+        '${indent * 2}final \$p = ReceivePort();\n'
+        '${indent * 2}final \$c = ${jni}Jni.newPortContinuation(\$p);\n'
+        '${indent * 2}$wrapperExpr;\n'
+        '${indent * 2}final \$o = $voidPointer.fromAddress(await \$p.first);\n'
+        '${indent * 2}final \$k = $returnTypeClass.getClass().reference;\n'
+        '${indent * 2}if (${jni}Jni.env.IsInstanceOf(\$o, \$k) == 0) {\n'
+        '${indent * 3}throw "Failed";\n'
+        '${indent * 2}}\n'
+        '${indent * 2}return ${toDartResult(
+          '\$o',
+          m.returnType,
+          returnTypeClass,
+        )};\n'
+        '}\n',
+      );
+    }
     return s.toString();
   }
 
@@ -227,10 +255,11 @@
       '// ignore_for_file: unused_shown_name\n'
       '\n';
 
+  static const dartImports = 'import "dart:isolate" show ReceivePort;\n\n';
   static const jniImport = 'import "package:jni/jni.dart" as jni;\n\n';
   static const internalHelpersImport =
       'import "package:jni/internal_helpers_for_jnigen.dart";\n\n';
-  static const defaultImports = jniImport + internalHelpersImport;
+  static const defaultImports = dartImports + jniImport + internalHelpersImport;
 
   static const initialization = 'final jniEnv = ${jni}Jni.env;\n'
       'final jniAccessors = ${jni}Jni.accessors;\n\n';
diff --git a/pkgs/jnigen/lib/src/config/config_types.dart b/pkgs/jnigen/lib/src/config/config_types.dart
index c32f824..9918b06 100644
--- a/pkgs/jnigen/lib/src/config/config_types.dart
+++ b/pkgs/jnigen/lib/src/config/config_types.dart
@@ -235,6 +235,7 @@
     required this.outputConfig,
     required this.classes,
     this.exclude,
+    this.suspendFunToAsync = false,
     this.sourcePath,
     this.classPath,
     this.preamble,
@@ -289,6 +290,12 @@
   /// `com.abc.package` -> 'package:abc/abc.dart'`
   final Map<String, String>? importMap;
 
+  /// Whether or not to change Kotlin's suspend functions to Dart async ones.
+  ///
+  /// This will remove the final Continuation argument.
+  /// Defaults to [false].
+  final bool suspendFunToAsync;
+
   /// Configuration to search for Android SDK libraries (Experimental).
   final AndroidSdkConfig? androidSdkConfig;
 
@@ -383,6 +390,7 @@
         methods: regexFilter<Method>(_Props.excludeMethods),
         fields: regexFilter<Field>(_Props.excludeFields),
       ),
+      suspendFunToAsync: prov.getBool(_Props.suspendFunToAsync) ?? false,
       outputConfig: OutputConfig(
         bindingsType: getBindingsType(
           prov.getString(_Props.bindingsType),
@@ -467,6 +475,8 @@
   static const excludeMethods = '$exclude.methods';
   static const excludeFields = '$exclude.fields';
 
+  static const suspendFunToAsync = 'suspend_fun_to_async';
+
   static const importMap = 'import_map';
   static const outputConfig = 'output';
   static const bindingsType = '$outputConfig.bindings_type';
diff --git a/pkgs/jnigen/lib/src/elements/elements.dart b/pkgs/jnigen/lib/src/elements/elements.dart
index 85269c2..1edefc4 100644
--- a/pkgs/jnigen/lib/src/elements/elements.dart
+++ b/pkgs/jnigen/lib/src/elements/elements.dart
@@ -58,7 +58,7 @@
     this.modifiers = const {},
     required this.simpleName,
     required this.binaryName,
-    required this.packageName,
+    this.packageName = '',
     this.parentName,
     this.typeParams = const [],
     this.methods = const [],
@@ -156,6 +156,12 @@
     required this.typeJson,
   });
 
+  static TypeUsage object = TypeUsage.fromJson({
+    "shorthand": "java.lang.Object",
+    "kind": "DECLARED",
+    "type": {"binaryName": "java.lang.Object", "simpleName": "Object"}
+  });
+
   String shorthand;
   Kind kind;
   @JsonKey(includeFromJson: false)
@@ -286,6 +292,13 @@
   late String finalName;
   @JsonKey(includeFromJson: false)
   late bool isOverridden;
+
+  /// This gets populated in the preprocessing stage.
+  ///
+  /// It will contain a type only when the suspendFunToAsync flag is on
+  /// and the method has a `kotlin.coroutines.Continuation` final argument.
+  @JsonKey(includeFromJson: false)
+  late TypeUsage? asyncReturnType;
   @JsonKey(includeFromJson: false)
   bool isIncluded = true;
 
diff --git a/pkgs/jnigen/lib/src/elements/elements.g.dart b/pkgs/jnigen/lib/src/elements/elements.g.dart
index 68f91d6..64e60f7 100644
--- a/pkgs/jnigen/lib/src/elements/elements.g.dart
+++ b/pkgs/jnigen/lib/src/elements/elements.g.dart
@@ -20,7 +20,7 @@
           const {},
       simpleName: json['simpleName'] as String,
       binaryName: json['binaryName'] as String,
-      packageName: json['packageName'] as String,
+      packageName: (json['packageName'] as String?) ?? '',
       parentName: json['parentName'] as String?,
       typeParams: (json['typeParams'] as List<dynamic>?)
               ?.map((e) => TypeParam.fromJson(e as Map<String, dynamic>))
diff --git a/pkgs/jnigen/lib/src/writers/files_writer.dart b/pkgs/jnigen/lib/src/writers/files_writer.dart
index 7361915..79904f0 100644
--- a/pkgs/jnigen/lib/src/writers/files_writer.dart
+++ b/pkgs/jnigen/lib/src/writers/files_writer.dart
@@ -236,7 +236,7 @@
         : PureDartBindingsGenerator(config);
 
     if (cBased) {
-      await writeCBindings(config, classes);
+      await writeCBindings(config, classesByName.values.toList());
     }
 
     // Write init file
diff --git a/pkgs/jnigen/lib/src/writers/single_file_writer.dart b/pkgs/jnigen/lib/src/writers/single_file_writer.dart
index 8d8e5c7..438f867 100644
--- a/pkgs/jnigen/lib/src/writers/single_file_writer.dart
+++ b/pkgs/jnigen/lib/src/writers/single_file_writer.dart
@@ -64,7 +64,7 @@
     ApiPreprocessor.preprocessAll(classesByName, config, renameClasses: true);
 
     if (cBased) {
-      await writeCBindings(config, classes);
+      await writeCBindings(config, classesByName.values.toList());
     }
     log.info("Generating ${cBased ? "C + Dart" : "Pure Dart"} Bindings");
     final generator = cBased
diff --git a/pkgs/jnigen/pubspec.yaml b/pkgs/jnigen/pubspec.yaml
index 0ac0eb5..b491b18 100644
--- a/pkgs/jnigen/pubspec.yaml
+++ b/pkgs/jnigen/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: jnigen
-version: 0.2.0
+version: 0.3.0
 description: Experimental generator for FFI+JNI bindings.
 repository: https://github.com/dart-lang/jnigen/tree/main/jnigen
 
@@ -11,7 +11,7 @@
   sdk: '>=2.17.0 <3.0.0'
 
 dependencies:
-  json_annotation: ^4.7.0
+  json_annotation: ^4.8.0
   package_config: ^2.1.0
   path: ^1.8.0
   args: ^2.3.0
@@ -24,5 +24,5 @@
     path: ../jni
   test: ^1.17.5
   build_runner: ^2.2.0
-  json_serializable: ^6.5.4
+  json_serializable: ^6.6.0
 
diff --git a/pkgs/jnigen/test/.gitignore b/pkgs/jnigen/test/.gitignore
new file mode 100644
index 0000000..6468101
--- /dev/null
+++ b/pkgs/jnigen/test/.gitignore
@@ -0,0 +1,2 @@
+# TODO(#166): Remove this.
+!jni.jar
\ No newline at end of file
diff --git a/pkgs/jnigen/test/bindings_test.dart b/pkgs/jnigen/test/bindings_test.dart
index 772d034..3c1cd9f 100644
--- a/pkgs/jnigen/test/bindings_test.dart
+++ b/pkgs/jnigen/test/bindings_test.dart
@@ -16,6 +16,7 @@
 import 'package:test/test.dart';
 
 // ignore_for_file: avoid_relative_lib_imports
+import 'kotlin_test/lib/kotlin.dart';
 import 'simple_package_test/lib/simple_package.dart';
 import 'jackson_core_test/third_party/lib/com/fasterxml/jackson/core/_package.dart';
 
@@ -23,7 +24,11 @@
 
 final simplePackageTest = join('test', 'simple_package_test');
 final jacksonCoreTest = join('test', 'jackson_core_test');
+final kotlinTest = join('test', 'kotlin_test');
+final jniJar = join(kotlinTest, 'jni.jar');
+
 final simplePackageTestJava = join(simplePackageTest, 'java');
+final kotlinTestKotlin = join(kotlinTest, 'kotlin');
 
 Future<void> setupDylibsAndClasses() async {
   await runCommand('dart', [
@@ -60,15 +65,38 @@
 
   final jacksonJars = await getJarPaths(join(jacksonCoreTest, 'third_party'));
 
+  await runCommand('dart', [
+    'run',
+    'jni:setup',
+    '-p',
+    'jni',
+    '-s',
+    join(kotlinTest, 'src'),
+  ]);
+  await runCommand(
+    'mvn',
+    ['package'],
+    workingDirectory: kotlinTestKotlin,
+    runInShell: true,
+  );
+  // Jar including Kotlin runtime and dependencies.
+  final kotlinTestJar =
+      join(kotlinTestKotlin, 'target', 'kotlin_test-jar-with-dependencies.jar');
+
   if (!Platform.isAndroid) {
-    Jni.spawn(
-        dylibDir: join('build', 'jni_libs'),
-        classPath: [simplePackageTestJava, ...jacksonJars]);
+    Jni.spawn(dylibDir: join('build', 'jni_libs'), classPath: [
+      jniJar,
+      simplePackageTestJava,
+      ...jacksonJars,
+      kotlinTestJar,
+    ]);
   }
+
+  Jni.initDLApi();
 }
 
 void main() async {
-  await setupDylibsAndClasses();
+  setUpAll(setupDylibsAndClasses);
 
   test('static final fields', () {
     expect(Example.ON, equals(1));
@@ -117,7 +145,6 @@
     final array = JArray(Example.type, 2);
     array[0] = ex1;
     array[1] = ex2;
-    print(array[0].getInternal());
     expect(array[0].getInternal(), 1);
     expect(array[1].getInternal(), 2);
     array.delete();
@@ -187,6 +214,7 @@
         expect(
           ((map.entryStack()..deletedIn(arena)).pop()..deletedIn(arena))
               .key
+              .castTo(JString.type, deleteOriginal: true)
               .toDartString(deleteOriginal: true),
           anyOf('Hello', 'World'),
         );
@@ -251,7 +279,9 @@
 
         final strParent = grandParent.stringParent()..deletedIn(arena);
         expect(
-          strParent.parentValue.toDartString(deleteOriginal: true),
+          strParent.parentValue
+              .castTo(JString.type, deleteOriginal: true)
+              .toDartString(deleteOriginal: true),
           "!",
         );
         expect(
@@ -263,7 +293,9 @@
             Example.type, Example()..deletedIn(arena))
           ..deletedIn(arena);
         expect(
-          exampleParent.parentValue.toDartString(deleteOriginal: true),
+          exampleParent.parentValue
+              .castTo(JString.type, deleteOriginal: true)
+              .toDartString(deleteOriginal: true),
           "!",
         );
         expect(
@@ -275,4 +307,17 @@
       });
     });
   });
+  group('Kotlin support', () {
+    test('Suspend functions', () async {
+      await using((arena) async {
+        final suspendFun = SuspendFun()..deletedIn(arena);
+        final hello = await suspendFun.sayHello();
+        expect(hello.toDartString(deleteOriginal: true), "Hello!");
+        const name = "Bob";
+        final helloBob =
+            await suspendFun.sayHello1(name.toJString()..deletedIn(arena));
+        expect(helloBob.toDartString(deleteOriginal: true), "Hello $name!");
+      });
+    });
+  });
 }
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
index be5fc9f..2cb23c0 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonFactory.dart
@@ -31,6 +31,8 @@
 // ignore_for_file: unused_import
 // ignore_for_file: unused_shown_name
 
+import "dart:isolate" show ReceivePort;
+
 import "package:jni/jni.dart" as jni;
 
 import "package:jni/internal_helpers_for_jnigen.dart";
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
index 2e1305e..4f45337 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonParser.dart
@@ -31,6 +31,8 @@
 // ignore_for_file: unused_import
 // ignore_for_file: unused_shown_name
 
+import "dart:isolate" show ReceivePort;
+
 import "package:jni/jni.dart" as jni;
 
 import "package:jni/internal_helpers_for_jnigen.dart";
diff --git a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
index e3aa9b6..8c3bc75 100644
--- a/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
+++ b/pkgs/jnigen/test/jackson_core_test/third_party/lib/com/fasterxml/jackson/core/JsonToken.dart
@@ -31,6 +31,8 @@
 // ignore_for_file: unused_import
 // ignore_for_file: unused_shown_name
 
+import "dart:isolate" show ReceivePort;
+
 import "package:jni/jni.dart" as jni;
 
 import "package:jni/internal_helpers_for_jnigen.dart";
diff --git a/pkgs/jnigen/test/kotlin_test/.gitignore b/pkgs/jnigen/test/kotlin_test/.gitignore
new file mode 100644
index 0000000..39de10f
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/.gitignore
@@ -0,0 +1,5 @@
+build/
+*.class
+test_lib/
+test_src/
+target/
diff --git a/pkgs/jnigen/test/kotlin_test/generate.dart b/pkgs/jnigen/test/kotlin_test/generate.dart
new file mode 100644
index 0000000..8f877ae
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/generate.dart
@@ -0,0 +1,70 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:logging/logging.dart';
+import 'package:path/path.dart';
+import 'package:jnigen/jnigen.dart';
+
+const testName = 'kotlin_test';
+const jarFile = '$testName.jar';
+
+final testRoot = join('test', testName);
+final kotlinPath = join(testRoot, 'kotlin');
+final jarPath = join(kotlinPath, 'target', jarFile);
+
+const preamble = '''
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+''';
+
+void compileKotlinSources(String workingDir) async {
+  final procRes = Process.runSync(
+    'mvn',
+    ['package'],
+    workingDirectory: workingDir,
+    runInShell: true,
+  );
+  if (procRes.exitCode != 0) {
+    throw "mvn exited with ${procRes.exitCode}\n"
+        "${procRes.stderr}";
+  }
+}
+
+Config getConfig([BindingsType bindingsType = BindingsType.cBased]) {
+  compileKotlinSources(kotlinPath);
+  final cWrapperDir = Uri.directory(join(testRoot, "src"));
+  final dartWrappersRoot = Uri.directory(join(testRoot, "lib"));
+  final config = Config(
+    classPath: [Uri.file(jarPath)],
+    classes: [
+      // Generating the entire library.
+      //
+      // This makes sure that no private class generated by Kotlin can make its
+      // way to the generated code.
+      'com.github.dart_lang.jnigen',
+    ],
+    suspendFunToAsync: true,
+    logLevel: Level.ALL,
+    outputConfig: OutputConfig(
+      bindingsType: bindingsType,
+      cConfig: CCodeOutputConfig(
+        path: cWrapperDir,
+        libraryName: 'kotlin',
+      ),
+      dartConfig: DartCodeOutputConfig(
+        path: dartWrappersRoot.resolve('kotlin.dart'),
+        structure: OutputStructure.singleFile,
+      ),
+    ),
+    summarizerOptions: SummarizerOptions(backend: 'asm'),
+    preamble: preamble,
+  );
+  return config;
+}
+
+void main() async => await generateJniBindings(getConfig());
diff --git a/pkgs/jnigen/test/kotlin_test/generated_files_test.dart b/pkgs/jnigen/test/kotlin_test/generated_files_test.dart
new file mode 100644
index 0000000..ee136e8
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/generated_files_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:jnigen/jnigen.dart';
+import 'package:test/test.dart';
+import 'package:path/path.dart' hide equals;
+
+import 'generate.dart';
+import '../test_util/test_util.dart';
+
+void main() async {
+  test("Generate and compare bindings for kotlin_test", () async {
+    await generateAndCompareBindings(
+      getConfig(),
+      join(testRoot, "lib", "kotlin.dart"),
+      join(testRoot, "src"),
+    );
+  }); // test if generated file == expected file
+  test("Generate and analyze bindings for kotlin_test - pure dart", () async {
+    await generateAndAnalyzeBindings(
+      getConfig(BindingsType.dartOnly),
+    );
+  }); // test if generated file == expected file
+}
diff --git a/pkgs/jnigen/test/kotlin_test/jni.jar b/pkgs/jnigen/test/kotlin_test/jni.jar
new file mode 100644
index 0000000..37a1bfc
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/jni.jar
Binary files differ
diff --git a/pkgs/jnigen/test/kotlin_test/kotlin/pom.xml b/pkgs/jnigen/test/kotlin_test/kotlin/pom.xml
new file mode 100644
index 0000000..b32d4ed
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/kotlin/pom.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>kotlin</artifactId>
+    <groupId>com.github.dart_lang.jnigen</groupId>
+    <version>1.0-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>consoleApp</name>
+
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <kotlin.code.style>official</kotlin.code.style>
+        <main.class>com.github.dart_lang.jnigen.SuspendFun</main.class>
+        <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
+    </properties>
+
+    <repositories>
+        <repository>
+            <id>mavenCentral</id>
+            <url>https://repo1.maven.org/maven2/</url>
+        </repository>
+    </repositories>
+
+    <build>
+        <finalName>kotlin_test</finalName>
+        <sourceDirectory>src/main/kotlin</sourceDirectory>
+        <testSourceDirectory>src/test/kotlin</testSourceDirectory>
+        <plugins>
+            <plugin>
+                <groupId>org.jetbrains.kotlin</groupId>
+                <artifactId>kotlin-maven-plugin</artifactId>
+                <version>1.7.20</version>
+                <executions>
+                    <execution>
+                        <id>compile</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>test-compile</id>
+                        <phase>test-compile</phase>
+                        <goals>
+                            <goal>test-compile</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.22.2</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <version>2.22.2</version>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>exec-maven-plugin</artifactId>
+                <version>1.6.0</version>
+                <configuration>
+                    <mainClass>MainKt</mainClass>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.6</version>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals> <goal>single</goal> </goals>
+                        <configuration>
+                            <archive>
+                                <manifest>
+                                    <mainClass>${main.class}</mainClass>
+                                </manifest>
+                            </archive>
+                            <descriptorRefs>
+                                <descriptorRef>jar-with-dependencies</descriptorRef>
+                            </descriptorRefs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-test-junit5</artifactId>
+            <version>1.7.20</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlinx</groupId>
+            <artifactId>kotlinx-coroutines-core</artifactId>
+            <version>1.6.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <version>5.8.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-stdlib-jdk8</artifactId>
+            <version>1.7.20</version>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/pkgs/jnigen/test/kotlin_test/kotlin/src/main/kotlin/com/github/dart_lang/jnigen/SuspendFun.kt b/pkgs/jnigen/test/kotlin_test/kotlin/src/main/kotlin/com/github/dart_lang/jnigen/SuspendFun.kt
new file mode 100644
index 0000000..ffd4376
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/kotlin/src/main/kotlin/com/github/dart_lang/jnigen/SuspendFun.kt
@@ -0,0 +1,21 @@
+/* Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+ * for details. All rights reserved. Use of this source code is governed by a
+ * BSD-style license that can be found in the LICENSE file.
+ */
+
+package com.github.dart_lang.jnigen
+
+import kotlinx.coroutines.delay
+import kotlin.coroutines.Continuation
+
+public class SuspendFun {
+    suspend fun sayHello(): String {
+        delay(100L)
+        return "Hello!"
+    }
+
+    suspend fun sayHello(name: String): String {
+        delay(100L)
+        return "Hello $name!"
+    }
+}
\ No newline at end of file
diff --git a/pkgs/jnigen/test/kotlin_test/lib/kotlin.dart b/pkgs/jnigen/test/kotlin_test/lib/kotlin.dart
new file mode 100644
index 0000000..aaf5b7e
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/lib/kotlin.dart
@@ -0,0 +1,115 @@
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Autogenerated by jnigen. DO NOT EDIT!
+
+// ignore_for_file: annotate_overrides
+// ignore_for_file: camel_case_extensions
+// ignore_for_file: camel_case_types
+// ignore_for_file: constant_identifier_names
+// ignore_for_file: file_names
+// ignore_for_file: no_leading_underscores_for_local_identifiers
+// ignore_for_file: non_constant_identifier_names
+// ignore_for_file: overridden_fields
+// ignore_for_file: unnecessary_cast
+// ignore_for_file: unused_element
+// ignore_for_file: unused_import
+
+import "dart:isolate" show ReceivePort;
+import "dart:ffi" as ffi;
+import "package:jni/internal_helpers_for_jnigen.dart";
+import "package:jni/jni.dart" as jni;
+
+// Auto-generated initialization code.
+
+final ffi.Pointer<T> Function<T extends ffi.NativeType>(String sym) jniLookup =
+    ProtectedJniExtensions.initGeneratedLibrary("kotlin");
+
+/// from: com.github.dart_lang.jnigen.SuspendFun
+class SuspendFun extends jni.JObject {
+  late final jni.JObjType? _$type;
+  @override
+  jni.JObjType get $type => _$type ??= type;
+
+  SuspendFun.fromRef(
+    jni.JObjectPtr ref,
+  ) : super.fromRef(ref);
+
+  /// The type which includes information such as the signature of this class.
+  static const type = $SuspendFunType();
+
+  static final _ctor = jniLookup<ffi.NativeFunction<jni.JniResult Function()>>(
+          "SuspendFun__ctor")
+      .asFunction<jni.JniResult Function()>();
+
+  /// from: public void <init>()
+  SuspendFun() : super.fromRef(_ctor().object);
+
+  static final _sayHello = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("SuspendFun__sayHello")
+      .asFunction<
+          jni.JniResult Function(
+              ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)>();
+
+  /// from: public final java.lang.Object sayHello(kotlin.coroutines.Continuation continuation)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  Future<jni.JString> sayHello() async {
+    final $p = ReceivePort();
+    final $c = jni.Jni.newPortContinuation($p);
+    _sayHello(reference, $c).object;
+    final $o = jni.JObjectPtr.fromAddress(await $p.first);
+    final $k = const jni.JStringType().getClass().reference;
+    if (jni.Jni.env.IsInstanceOf($o, $k) == 0) {
+      throw "Failed";
+    }
+    return const jni.JStringType().fromRef($o);
+  }
+
+  static final _sayHello1 = jniLookup<
+          ffi.NativeFunction<
+              jni.JniResult Function(
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>,
+                  ffi.Pointer<ffi.Void>)>>("SuspendFun__sayHello1")
+      .asFunction<
+          jni.JniResult Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>,
+              ffi.Pointer<ffi.Void>)>();
+
+  /// from: public final java.lang.Object sayHello(java.lang.String string, kotlin.coroutines.Continuation continuation)
+  /// The returned object must be deleted after use, by calling the `delete` method.
+  Future<jni.JString> sayHello1(jni.JString string) async {
+    final $p = ReceivePort();
+    final $c = jni.Jni.newPortContinuation($p);
+    _sayHello1(reference, string.reference, $c).object;
+    final $o = jni.JObjectPtr.fromAddress(await $p.first);
+    final $k = const jni.JStringType().getClass().reference;
+    if (jni.Jni.env.IsInstanceOf($o, $k) == 0) {
+      throw "Failed";
+    }
+    return const jni.JStringType().fromRef($o);
+  }
+}
+
+class $SuspendFunType extends jni.JObjType<SuspendFun> {
+  const $SuspendFunType();
+
+  @override
+  String get signature => r"Lcom/github/dart_lang/jnigen/SuspendFun;";
+
+  @override
+  SuspendFun fromRef(jni.JObjectPtr ref) => SuspendFun.fromRef(ref);
+}
+
+extension $SuspendFunArray on jni.JArray<SuspendFun> {
+  SuspendFun operator [](int index) {
+    return (elementType as $SuspendFunType)
+        .fromRef(elementAt(index, jni.JniCallType.objectType).object);
+  }
+
+  void operator []=(int index, SuspendFun value) {
+    (this as jni.JArray<jni.JObject>)[index] = value;
+  }
+}
diff --git a/pkgs/jnigen/test/kotlin_test/src/.clang-format b/pkgs/jnigen/test/kotlin_test/src/.clang-format
new file mode 100644
index 0000000..a256c2f
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/src/.clang-format
@@ -0,0 +1,15 @@
+# From dart SDK: https://github.com/dart-lang/sdk/blob/main/.clang-format
+
+# Defines the Chromium style for automatic reformatting.
+# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
+BasedOnStyle: Chromium
+
+# clang-format doesn't seem to do a good job of this for longer comments.
+ReflowComments: 'false'
+
+# We have lots of these. Though we need to put them all in curly braces,
+# clang-format can't do that.
+AllowShortIfStatementsOnASingleLine: 'true'
+
+# Put escaped newlines into the rightmost column.
+AlignEscapedNewlinesLeft: false
diff --git a/pkgs/jnigen/test/kotlin_test/src/CMakeLists.txt b/pkgs/jnigen/test/kotlin_test/src/CMakeLists.txt
new file mode 100644
index 0000000..3889f54
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/src/CMakeLists.txt
@@ -0,0 +1,32 @@
+# jni_native_build (Build with jni:setup. Do not delete this line.)
+
+# The Flutter tooling requires that developers have CMake 3.10 or later
+# installed. You should not increase this version, as doing so will cause
+# the plugin to fail to compile for some customers of the plugin.
+cmake_minimum_required(VERSION 3.10)
+
+project(kotlin VERSION 0.0.1 LANGUAGES C)
+
+add_library(kotlin SHARED
+  "./kotlin.c"
+)
+
+set_target_properties(kotlin PROPERTIES
+  OUTPUT_NAME "kotlin"
+)
+
+target_compile_definitions(kotlin PUBLIC DART_SHARED_LIB)
+
+if(WIN32)
+	set_target_properties(${TARGET_NAME} PROPERTIES
+		LINK_FLAGS "/DELAYLOAD:jvm.dll")
+endif()
+
+if (ANDROID)
+	target_link_libraries(kotlin log)
+else()
+	find_package(Java REQUIRED)
+	find_package(JNI REQUIRED)
+	include_directories(${JNI_INCLUDE_DIRS})
+	target_link_libraries(kotlin ${JNI_LIBRARIES})
+endif()
diff --git a/pkgs/jnigen/test/kotlin_test/src/dartjni.h b/pkgs/jnigen/test/kotlin_test/src/dartjni.h
new file mode 100644
index 0000000..39b70bd
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/src/dartjni.h
@@ -0,0 +1,272 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#pragma once
+
+// Note: include appropriate system jni.h as found by CMake, not third_party/jni.h.
+#include <jni.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if _WIN32
+#include <windows.h>
+#else
+#include <pthread.h>
+#include <unistd.h>
+#endif
+
+#if _WIN32
+#define FFI_PLUGIN_EXPORT __declspec(dllexport)
+#else
+#define FFI_PLUGIN_EXPORT
+#endif
+
+#if defined _WIN32
+#define thread_local __declspec(thread)
+#else
+#define thread_local __thread
+#endif
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
+#ifdef __ANDROID__
+#define __ENVP_CAST (JNIEnv**)
+#else
+#define __ENVP_CAST (void**)
+#endif
+
+/// Stores the global state of the JNI.
+typedef struct JniContext {
+  JavaVM* jvm;
+  jobject classLoader;
+  jmethodID loadClassMethod;
+  jobject currentActivity;
+  jobject appContext;
+} JniContext;
+
+// jniEnv for this thread, used by inline functions in this header,
+// therefore declared as extern.
+extern thread_local JNIEnv* jniEnv;
+
+extern JniContext jni;
+
+/// Types used by JNI API to distinguish between primitive types.
+enum JniType {
+  booleanType = 0,
+  byteType = 1,
+  shortType = 2,
+  charType = 3,
+  intType = 4,
+  longType = 5,
+  floatType = 6,
+  doubleType = 7,
+  objectType = 8,
+  voidType = 9,
+};
+
+/// Result type for use by JNI.
+///
+/// If [exception] is null, it means the result is valid.
+/// It's assumed that the caller knows the expected type in [result].
+typedef struct JniResult {
+  jvalue result;
+  jthrowable exception;
+} JniResult;
+
+/// Similar to [JniResult] but for class lookups.
+typedef struct JniClassLookupResult {
+  jclass classRef;
+  jthrowable exception;
+} JniClassLookupResult;
+
+/// Similar to [JniResult] but for method/field ID lookups.
+typedef struct JniPointerResult {
+  void* id;
+  jthrowable exception;
+} JniPointerResult;
+
+/// JniExceptionDetails holds 2 jstring objects, one is the result of
+/// calling `toString` on exception object, other is stack trace;
+typedef struct JniExceptionDetails {
+  jstring message;
+  jstring stacktrace;
+} JniExceptionDetails;
+
+/// This struct contains functions which wrap method call / field access conveniently along with
+/// exception checking.
+///
+/// Flutter embedding checks for pending JNI exceptions before an FFI transition, which requires us
+/// to check for and clear the exception before returning to dart code, which requires these functions
+/// to return result types.
+typedef struct JniAccessors {
+  JniClassLookupResult (*getClass)(char* internalName);
+  JniPointerResult (*getFieldID)(jclass cls, char* fieldName, char* signature);
+  JniPointerResult (*getStaticFieldID)(jclass cls,
+                                       char* fieldName,
+                                       char* signature);
+  JniPointerResult (*getMethodID)(jclass cls,
+                                  char* methodName,
+                                  char* signature);
+  JniPointerResult (*getStaticMethodID)(jclass cls,
+                                        char* methodName,
+                                        char* signature);
+  JniResult (*newObject)(jclass cls, jmethodID ctor, jvalue* args);
+  JniPointerResult (*newPrimitiveArray)(jsize length, int type);
+  JniPointerResult (*newObjectArray)(jsize length,
+                                     jclass elementClass,
+                                     jobject initialElement);
+  JniResult (*getArrayElement)(jarray array, int index, int type);
+  JniResult (*callMethod)(jobject obj,
+                          jmethodID methodID,
+                          int callType,
+                          jvalue* args);
+  JniResult (*callStaticMethod)(jclass cls,
+                                jmethodID methodID,
+                                int callType,
+                                jvalue* args);
+  JniResult (*getField)(jobject obj, jfieldID fieldID, int callType);
+  JniResult (*getStaticField)(jclass cls, jfieldID fieldID, int callType);
+  JniExceptionDetails (*getExceptionDetails)(jthrowable exception);
+} JniAccessors;
+
+FFI_PLUGIN_EXPORT JniAccessors* GetAccessors();
+
+FFI_PLUGIN_EXPORT JavaVM* GetJavaVM(void);
+
+FFI_PLUGIN_EXPORT JNIEnv* GetJniEnv(void);
+
+FFI_PLUGIN_EXPORT JNIEnv* SpawnJvm(JavaVMInitArgs* args);
+
+FFI_PLUGIN_EXPORT jclass LoadClass(const char* name);
+
+FFI_PLUGIN_EXPORT jobject GetClassLoader(void);
+
+FFI_PLUGIN_EXPORT jobject GetApplicationContext(void);
+
+FFI_PLUGIN_EXPORT jobject GetCurrentActivity(void);
+
+// Migration note: Below inline functions are required by C bindings, but can be moved to dartjni.c
+// once migration to pure dart bindings is complete.
+
+// `static inline` because `inline` doesn't work, it may still not
+// inline the function in which case a linker error may be produced.
+//
+// There has to be a better way to do this. Either to force inlining on target
+// platforms, or just leave it as normal function.
+
+static inline void __load_class_into(jclass* cls, const char* name) {
+#ifdef __ANDROID__
+  jstring className = (*jniEnv)->NewStringUTF(jniEnv, name);
+  *cls = (*jniEnv)->CallObjectMethod(jniEnv, jni.classLoader,
+                                     jni.loadClassMethod, className);
+  (*jniEnv)->DeleteLocalRef(jniEnv, className);
+#else
+  *cls = (*jniEnv)->FindClass(jniEnv, name);
+#endif
+}
+
+static inline void load_class(jclass* cls, const char* name) {
+  if (*cls == NULL) {
+    __load_class_into(cls, name);
+  }
+}
+
+static inline void load_class_gr(jclass* cls, const char* name) {
+  if (*cls == NULL) {
+    jclass tmp;
+    __load_class_into(&tmp, name);
+    *cls = (*jniEnv)->NewGlobalRef(jniEnv, tmp);
+    (*jniEnv)->DeleteLocalRef(jniEnv, tmp);
+  }
+}
+
+static inline void attach_thread() {
+  if (jniEnv == NULL) {
+    (*jni.jvm)->AttachCurrentThread(jni.jvm, __ENVP_CAST & jniEnv, NULL);
+  }
+}
+
+static inline void load_method(jclass cls,
+                               jmethodID* res,
+                               const char* name,
+                               const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetMethodID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline void load_static_method(jclass cls,
+                                      jmethodID* res,
+                                      const char* name,
+                                      const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetStaticMethodID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline void load_field(jclass cls,
+                              jfieldID* res,
+                              const char* name,
+                              const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetFieldID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline void load_static_field(jclass cls,
+                                     jfieldID* res,
+                                     const char* name,
+                                     const char* sig) {
+  if (*res == NULL) {
+    *res = (*jniEnv)->GetStaticFieldID(jniEnv, cls, name, sig);
+  }
+}
+
+static inline jobject to_global_ref(jobject ref) {
+  jobject g = (*jniEnv)->NewGlobalRef(jniEnv, ref);
+  (*jniEnv)->DeleteLocalRef(jniEnv, ref);
+  return g;
+}
+
+// These functions are useful for C+Dart bindings, and not required for pure dart bindings.
+
+FFI_PLUGIN_EXPORT JniContext GetJniContext();
+/// For use by jni_gen's generated code
+/// don't use these.
+
+// these 2 fn ptr vars will be defined by generated code library
+extern JniContext (*context_getter)(void);
+extern JNIEnv* (*env_getter)(void);
+
+// this function will be exported by generated code library
+// it will set above 2 variables.
+FFI_PLUGIN_EXPORT void setJniGetters(struct JniContext (*cg)(void),
+                                     JNIEnv* (*eg)(void));
+
+static inline void load_env() {
+  if (jniEnv == NULL) {
+    jni = context_getter();
+    jniEnv = env_getter();
+  }
+}
+
+static inline jthrowable check_exception() {
+  jthrowable exception = (*jniEnv)->ExceptionOccurred(jniEnv);
+  if (exception != NULL) (*jniEnv)->ExceptionClear(jniEnv);
+  if (exception == NULL) return NULL;
+  return to_global_ref(exception);
+}
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result);
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j);
diff --git a/pkgs/jnigen/test/kotlin_test/src/kotlin.c b/pkgs/jnigen/test/kotlin_test/src/kotlin.c
new file mode 100644
index 0000000..7b1426c
--- /dev/null
+++ b/pkgs/jnigen/test/kotlin_test/src/kotlin.c
@@ -0,0 +1,76 @@
+// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Autogenerated by jnigen. DO NOT EDIT!
+
+#include <stdint.h>
+#include "dartjni.h"
+#include "jni.h"
+
+thread_local JNIEnv* jniEnv;
+JniContext jni;
+
+JniContext (*context_getter)(void);
+JNIEnv* (*env_getter)(void);
+
+void setJniGetters(JniContext (*cg)(void), JNIEnv* (*eg)(void)) {
+  context_getter = cg;
+  env_getter = eg;
+}
+
+// com.github.dart_lang.jnigen.SuspendFun
+jclass _c_SuspendFun = NULL;
+
+jmethodID _m_SuspendFun__ctor = NULL;
+FFI_PLUGIN_EXPORT
+JniResult SuspendFun__ctor() {
+  load_env();
+  load_class_gr(&_c_SuspendFun, "com/github/dart_lang/jnigen/SuspendFun");
+  if (_c_SuspendFun == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_SuspendFun, &_m_SuspendFun__ctor, "<init>", "()V");
+  if (_m_SuspendFun__ctor == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result =
+      (*jniEnv)->NewObject(jniEnv, _c_SuspendFun, _m_SuspendFun__ctor);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_SuspendFun__sayHello = NULL;
+FFI_PLUGIN_EXPORT
+JniResult SuspendFun__sayHello(jobject self_, jobject continuation) {
+  load_env();
+  load_class_gr(&_c_SuspendFun, "com/github/dart_lang/jnigen/SuspendFun");
+  if (_c_SuspendFun == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(_c_SuspendFun, &_m_SuspendFun__sayHello, "sayHello",
+              "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;");
+  if (_m_SuspendFun__sayHello == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallObjectMethod(
+      jniEnv, self_, _m_SuspendFun__sayHello, continuation);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
+
+jmethodID _m_SuspendFun__sayHello1 = NULL;
+FFI_PLUGIN_EXPORT
+JniResult SuspendFun__sayHello1(jobject self_,
+                                jobject string,
+                                jobject continuation) {
+  load_env();
+  load_class_gr(&_c_SuspendFun, "com/github/dart_lang/jnigen/SuspendFun");
+  if (_c_SuspendFun == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  load_method(
+      _c_SuspendFun, &_m_SuspendFun__sayHello1, "sayHello",
+      "(Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;");
+  if (_m_SuspendFun__sayHello1 == NULL)
+    return (JniResult){.result = {.j = 0}, .exception = check_exception()};
+  jobject _result = (*jniEnv)->CallObjectMethod(
+      jniEnv, self_, _m_SuspendFun__sayHello1, string, continuation);
+  return (JniResult){.result = {.l = to_global_ref(_result)},
+                     .exception = check_exception()};
+}
diff --git a/pkgs/jnigen/test/regenerate_examples_test.dart b/pkgs/jnigen/test/regenerate_examples_test.dart
index 0f28fed..b49da06 100644
--- a/pkgs/jnigen/test/regenerate_examples_test.dart
+++ b/pkgs/jnigen/test/regenerate_examples_test.dart
@@ -16,6 +16,8 @@
 final inAppJavaYaml = join(inAppJava, 'jnigen.yaml');
 final notificationPlugin = join('example', 'notification_plugin');
 final notificationPluginYaml = join(notificationPlugin, 'jnigen.yaml');
+final kotlinPlugin = join('example', 'kotlin_plugin');
+final kotlinPluginYaml = join(kotlinPlugin, 'jnigen.yaml');
 
 /// Generates bindings using jnigen config in [exampleName] and compares
 /// them to provided reference outputs.
@@ -54,4 +56,9 @@
     join('lib', 'notifications.dart'),
     'src',
   );
+  testExample(
+    'kotlin_plugin',
+    join('lib', 'kotlin_bindings.dart'),
+    'src',
+  );
 }
diff --git a/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart b/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
index aa79dbe..0f6841c 100644
--- a/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
+++ b/pkgs/jnigen/test/simple_package_test/lib/simple_package.dart
@@ -16,6 +16,7 @@
 // ignore_for_file: unused_element
 // ignore_for_file: unused_import
 
+import "dart:isolate" show ReceivePort;
 import "dart:ffi" as ffi;
 import "package:jni/internal_helpers_for_jnigen.dart";
 import "package:jni/jni.dart" as jni;
@@ -468,8 +469,8 @@
 
   /// from: public com.github.dart_lang.jnigen.generics.GrandParent<T>.Parent<java.lang.String> stringParent()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  GrandParent_Parent<T, jni.JString> stringParent() =>
-      $GrandParent_ParentType($T, jni.JStringType())
+  GrandParent_Parent<jni.JObject, jni.JString> stringParent() =>
+      $GrandParent_ParentType(const jni.JObjectType(), jni.JStringType())
           .fromRef(_stringParent(reference).object);
 
   static final _varParent = jniLookup<
@@ -482,9 +483,9 @@
 
   /// from: public com.github.dart_lang.jnigen.generics.GrandParent<T>.Parent<S> varParent(S nestedValue)
   /// The returned object must be deleted after use, by calling the `delete` method.
-  GrandParent_Parent<T, S> varParent<S extends jni.JObject>(
+  GrandParent_Parent<jni.JObject, S> varParent<S extends jni.JObject>(
           jni.JObjType<S> $S, S nestedValue) =>
-      $GrandParent_ParentType($T, $S)
+      $GrandParent_ParentType(const jni.JObjectType(), $S)
           .fromRef(_varParent(reference, nestedValue.reference).object);
 
   static final _stringStaticParent =
@@ -1126,9 +1127,9 @@
 
   /// from: public com.github.dart_lang.jnigen.generics.MyStack<com.github.dart_lang.jnigen.generics.MyMap<K,V>.MyEntry> entryStack()
   /// The returned object must be deleted after use, by calling the `delete` method.
-  MyStack<MyMap_MyEntry<K, V>> entryStack() =>
-      $MyStackType($MyMap_MyEntryType($K, $V))
-          .fromRef(_entryStack(reference).object);
+  MyStack<MyMap_MyEntry<jni.JObject, jni.JObject>> entryStack() => $MyStackType(
+          $MyMap_MyEntryType(const jni.JObjectType(), const jni.JObjectType()))
+      .fromRef(_entryStack(reference).object);
 }
 
 class $MyMapType<K extends jni.JObject, V extends jni.JObject>
diff --git a/pkgs/jnigen/test/simple_package_test/src/dartjni.h b/pkgs/jnigen/test/simple_package_test/src/dartjni.h
index 12e38b9..39b70bd 100644
--- a/pkgs/jnigen/test/simple_package_test/src/dartjni.h
+++ b/pkgs/jnigen/test/simple_package_test/src/dartjni.h
@@ -260,3 +260,13 @@
   if (exception == NULL) return NULL;
   return to_global_ref(exception);
 }
+
+FFI_PLUGIN_EXPORT intptr_t InitDartApiDL(void* data);
+
+JNIEXPORT void JNICALL
+Java_com_github_dart_1lang_jni_PortContinuation__1resumeWith(JNIEnv* env,
+                                                             jobject thiz,
+                                                             jlong port,
+                                                             jobject result);
+FFI_PLUGIN_EXPORT
+JniResult PortContinuation__ctor(int64_t j);
diff --git a/pkgs/jnigen/test/test_util/test_util.dart b/pkgs/jnigen/test/test_util/test_util.dart
index df6b1b9..5169684 100644
--- a/pkgs/jnigen/test/test_util/test_util.dart
+++ b/pkgs/jnigen/test/test_util/test_util.dart
@@ -18,9 +18,9 @@
 
 /// Runs command, and prints output only if the exit status is non-zero.
 Future<int> runCommand(String exec, List<String> args,
-    {String? workingDirectory}) async {
-  final proc =
-      await Process.run(exec, args, workingDirectory: workingDirectory);
+    {String? workingDirectory, bool runInShell = false}) async {
+  final proc = await Process.run(exec, args,
+      workingDirectory: workingDirectory, runInShell: runInShell);
   if (proc.exitCode != 0) {
     printError('command exited with exit status ${proc.exitCode}:\n'
         '$exec ${args.join(" ")}\n');
diff --git a/pkgs/jnigen/tool/regenerate_all_bindings.dart b/pkgs/jnigen/tool/regenerate_all_bindings.dart
index b99fb01..19b7642 100644
--- a/pkgs/jnigen/tool/regenerate_all_bindings.dart
+++ b/pkgs/jnigen/tool/regenerate_all_bindings.dart
@@ -13,12 +13,14 @@
 const scripts = [
   "test/jackson_core_test/generate.dart",
   "test/simple_package_test/generate.dart",
+  "test/kotlin_test/generate.dart",
 ];
 
 const yamlBasedExamples = [
   "example/in_app_java",
   "example/pdfbox_plugin",
   "example/notification_plugin",
+  "example/kotlin_plugin",
 ];
 
 void main() async {