NOTEThis article was translated by ChatGPT.
The following excerpt is from the QuickJS official website.
QuickJS is a small and embeddable JavaScript engine. It supports the ES2020 specification, including modules, async generators, proxies, and BigInt. It optionally supports mathematical extensions such as BigDecimal, BigFloat, and operator overloading.
Key features:
Flutter apps are written in Dart. To interface with C libraries, Dart uses the dart:ffi
library.
dart:ffi
is designed for calling native C APIs. FFI stands for Foreign Function Interface. For detailed usage, refer to the official documentation.
Since Flutter is cross-platform, you need to compile QuickJS into platform-specific dynamic libraries.
.dll
file..so
file.This section focuses on building for Windows and Android (Linux builds are simpler and omitted).
pacman -S mingw-w64-x86_64-gcc mingw-w64-x86_64-make mingw-w64-x86_64-dlfcn
echo "#! /bin/sh" > /mingw64/bin/make
echo "\"mingw32-make\" \"\$@\"" >> /mingw64/bin/make
pacman -S mingw-w64-i686-gcc mingw-w64-i686-make mingw-w64-i686-dlfcn
echo "#! /bin/sh" > /mingw32/bin/make
echo "\"mingw32-make\" \"\$@\"" >> /mingw32/bin/make
git clone https://github.com/bellard/quickjs.git
cd quickjs && make
make
finishes, you’ll get libquickjs.a
. Create the DLL:gcc -shared -o libquickjs.dll -static -s -Wl,--whole-archive libquickjs.a -lm -Wl,--no-whole-archive
For Android, you’ll need a CMakeLists.txt
to compile the C code:
cmake_minimum_required(VERSION 3.4.1)
project(quickjs LANGUAGES C)
include_directories(quickjs)
set(QUICK_JS_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../quickjs)
set(
SOURCE_DIR
${QUICK_JS_DIR}/cutils.c
${QUICK_JS_DIR}/libbf.c
${QUICK_JS_DIR}/libregexp.c
${QUICK_JS_DIR}/libunicode.c
${QUICK_JS_DIR}/quickjs.c
${QUICK_JS_DIR}/quickjs-libc.c
)
file(STRINGS "${QUICK_JS_DIR}/VERSION" CONFIG_VERSION)
add_definitions(-DCONFIG_VERSION="${CONFIG_VERSION}")
add_definitions(-DCONFIG_BIGNUM)
add_definitions(-D_GNU_SOURCE)
add_definitions(-DCONFIG_CC="gcc")
add_definitions(-DCONFIG_PREFIX="/usr/local")
add_library(
${PROJECT_NAME}
SHARED
${SOURCE_DIR}
)
target_include_directories(${PROJECT_NAME} PUBLIC .)
Place this file under android/src/main/cpp
in your Flutter project, and put the QuickJS source in the project root. When building for Android, Flutter will compile and package libquickjs.so
automatically.
For details, see the official guide on adding C/C++ code to Android.
To call C/C++ functions from Dart, you must first declare them in Dart. For example, given a C function:
int add(int a, int b) {
return a + b;
}
You’d declare it in Dart as:
import 'dart:ffi' as ffi;
final nativeAddFunc =
dynamicLibrary.lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>('add');
dynamicLibrary.lookup
finds a C function by its name, return type, and parameter types.
Declaring foreign functions in another language is known as language bindings.
Manually declaring every QuickJS function is tedious. Fortunately, Dart provides the ffigen
package, which automatically generates bindings from header files.
Steps to generate QuickJS bindings:
flutter pub add ffigen
pubspec.yaml
:ffigen:
name: QuickJSBindings
description: generate bindings for quick js
output: lib/bindings.dart
headers:
entry-points:
- quickjs/quickjs.h
- quickjs/quickjs-libc.h
dart run ffigen
final _lib = DynamicLibrary.open(libquickjs);
final _ = QuickJSBindings(_lib);
JSRuntime
and JSContext
:final _runtime = _.JS_NewRuntime();
final _context = _.JS_NewContext(_runtime);
JS_Eval
:const flag = JS_EVAL_FLAG_STRICT;
final input = code.toNativeUtf8().cast<Char>();
final name = filename.toNativeUtf8().cast<Char>();
final inputLen = _getPtrCharLen(input);
final jsValue = _.JS_Eval(_context, input, inputLen, name, flag);
calloc.free(input);
calloc.free(name);
final result = _js2Dart(_context, jsValue);
_jsStdLoop(_context);
_jsFreeValue(_context, jsValue);
if (result is Exception) {
throw ret;
}
That’s the basic usage. Handling promises, implementing event loops, and other advanced topics are beyond the scope of this introductory article.