All Projects → ekibun → flutter_qjs

ekibun / flutter_qjs

Licence: MIT License
A quickjs engine for flutter.

Programming Languages

dart
5743 projects
C++
36643 projects - #6 most used programming language
CMake
9771 projects
c
50402 projects - #5 most used programming language
ruby
36898 projects - #4 most used programming language
swift
15916 projects

Projects that are alternatives of or similar to flutter qjs

quickwebserver
Implementation of HTTP web server in the QuickJS Runtime
Stars: ✭ 17 (-81.91%)
Mutual labels:  quickjs
quickjs-build
Build for QuickJS JavaScript Engine
Stars: ✭ 25 (-73.4%)
Mutual labels:  quickjs
jsqry-cli2
Small CLI tool (similar to jq) to query JSON using sane DSL
Stars: ✭ 21 (-77.66%)
Mutual labels:  quickjs
quickjs es runtime
this is a wrapper library for the javascript runtime quickjs written in rust which works with modules, promises, async, await and much more
Stars: ✭ 43 (-54.26%)
Mutual labels:  quickjs
Elsa
❄️ Elsa is a minimal, fast and secure runtime for JavaScript and TypeScript written in Go
Stars: ✭ 2,461 (+2518.09%)
Mutual labels:  quickjs
Quickjs
QuickJS是一个小型并且可嵌入的Javascript引擎,它支持ES2020规范,包括模块,异步生成器和代理器。
Stars: ✭ 2,199 (+2239.36%)
Mutual labels:  quickjs

flutter_qjs

Pub Test

English | 中文

This plugin is a simple js engine for flutter using the quickjs project with dart:ffi. Plugin currently supports all the platforms except web!

Getting Started

Basic usage

Firstly, create a FlutterQjs object, then call dispatch to establish event loop:

final engine = FlutterQjs(
  stackSize: 1024 * 1024, // change stack size here.
);
engine.dispatch();

Use evaluate method to run js script, it runs synchronously, you can use await to resolve Promise:

try {
  print(engine.evaluate(code ?? ''));
} catch (e) {
  print(e.toString());
}

Method close can destroy quickjs runtime that can be recreated again if you call evaluate. Parameter port should be close to stop dispatch loop when you do not need it. Reference leak exception will be thrown since v0.3.3

try {
  engine.port.close(); // stop dispatch loop
  engine.close();      // close engine
} on JSError catch(e) { 
  print(e);            // catch reference leak exception
}
engine = null;

Data conversion between dart and js are implemented as follow:

dart js
Bool boolean
Int number
Double number
String string
Uint8List ArrayBuffer
List Array
Map Object
Function(arg1, arg2, ..., {thisVal})
JSInvokable.invoke([arg1, arg2, ...], thisVal)
function.call(thisVal, arg1, arg2, ...)
Future Promise
JSError Error
Object DartObject

Use Modules

ES6 module with import function is supported and can be managed in dart with moduleHandler:

final engine = FlutterQjs(
  moduleHandler: (String module) {
    if(module == "hello")
      return "export default (name) => `hello \${name}!`;";
    throw Exception("Module Not found");
  },
);

then in JavaScript, import function is used to get modules:

import("hello").then(({default: greet}) => greet("world"));

notice: Module handler should be called only once for each module name. To reset the module cache, call FlutterQjs.close then evaluate again.

To use async function in module handler, try run on isolate thread

Run on Isolate Thread

Create a IsolateQjs object, pass handlers to resolving modules. Async function such as rootBundle.loadString can be used now to get modules:

final engine = IsolateQjs(
  moduleHandler: (String module) async {
    return await rootBundle.loadString(
        "js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
  },
);
// not need engine.dispatch();

Same as run on main thread, use evaluate to run js script. In isolate, everything returns asynchronously, use await to get the result:

try {
  print(await engine.evaluate(code ?? ''));
} catch (e) {
  print(e.toString());
}

Method close can destroy isolate thread that will be recreated again if you call evaluate.

Use Dart Function (Breaking change in v0.3.0)

Js script returning function will be converted to JSInvokable. It does not extend Function, use invoke method to invoke it:

(func as JSInvokable).invoke([arg1, arg2], thisVal);

notice: evaluation returning JSInvokable may cause reference leak. You should manually call free to release JS reference.

(obj as JSRef).free();
// or JSRef.freeRecursive(obj);

Arguments passed into JSInvokable will be freed automatically. Use dup to keep the reference.

(obj as JSRef).dup();
// or JSRef.dupRecursive(obj);

Since v0.3.0, you can pass a function to JSInvokable arguments, and channel function is no longer included by default. You can use js function to set dart object globally. For example, use Dio to implement http in qjs:

final setToGlobalObject = await engine.evaluate("(key, val) => { this[key] = val; }");
await setToGlobalObject.invoke(["http", (String url) {
  return Dio().get(url).then((response) => response.data);
}]);
setToGlobalObject.free();

In isolate, top level function passed in JSInvokable will be invoked in isolate thread. Use IsolateFunction to pass a instant function:

await setToGlobalObject.invoke([
  "http",
  IsolateFunction((String url) {
    return Dio().get(url).then((response) => response.data);
  }),
]);
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].