diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1698fb..bd07a4b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,12 @@
* @LastEditTime: 2020-12-02 11:36:40
-->
+## 0.3.3
+
+* remove `JSInvokable.call`.
+* fix crash when throw error.
+* add reference count and leak detection.
+
## 0.3.2
* fix Promise reject cannot get Exception string.
diff --git a/README-CN.md b/README-CN.md
new file mode 100644
index 0000000..70b75b6
--- /dev/null
+++ b/README-CN.md
@@ -0,0 +1,157 @@
+
+# flutter_qjs
+
+
+
+
+[English](README.md) | [中文](README-CN.md)
+
+一个为flutter开发的 `quickjs` 引擎。插件基于 `dart:ffi`,支持除Web以外的所有平台!
+
+## 基本使用
+
+首先,创建 `FlutterQjs` 对象。调用 `dispatch` 建立事件循环:
+
+```dart
+final engine = FlutterQjs(
+ stackSize: 1024 * 1024, // change stack size here.
+);
+engine.dispatch();
+```
+
+使用 `evaluate` 方法运行js脚本,方法同步执行,使用 `await` 来获得 `Promise` 结果:
+
+```dart
+try {
+ print(engine.evaluate(code ?? ''));
+} catch (e) {
+ print(e.toString());
+}
+```
+
+使用 `close` 方法销毁 quickjs 实例,其在再次调用 `evaluate` 时将会重建。当不再需要 `FlutterQjs` 对象时,关闭 `port` 参数来结束事件循环。**在 v0.3.3 后增加了引用检查,可能会抛出异常**。
+
+```dart
+try {
+ engine.port.close(); // stop dispatch loop
+ engine.close(); // close engine
+} on JSError catch(e) {
+ print(e); // catch reference leak exception
+}
+engine = null;
+```
+
+dart 与 js 间数据以如下规则转换:
+
+| 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 |
+
+## 使用模块
+
+插件支持 ES6 模块方法 `import`。使用 `moduleHandler` 来处理模块请求:
+
+```dart
+final engine = FlutterQjs(
+ moduleHandler: (String module) {
+ if(module == "hello")
+ return "export default (name) => `hello \${name}!`;";
+ throw Exception("Module Not found");
+ },
+);
+```
+
+在JavaScript中,`import` 方法用以获取模块:
+
+```javascript
+import("hello").then(({default: greet}) => greet("world"));
+```
+
+**注:** 模块将只被编译一次. 调用 `FlutterQjs.close` 再 `evaluate` 来重置模块缓存。
+
+若要使用异步方法来处理模块请求,请参见 [在 isolate 中运行](#在-isolate-中运行)。
+
+## 在 isolate 中运行
+
+创建 `IsolateQjs` 对象,设置 `moduleHandler` 来处理模块请求。 现在可以使用异步函数来获得模块字符串,如 `rootBundle.loadString`:
+
+```dart
+final engine = IsolateQjs(
+ moduleHandler: (String module) async {
+ return await rootBundle.loadString(
+ "js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
+ },
+);
+// not need engine.dispatch();
+```
+
+与在主线程运行一样,使用 `evaluate` 方法运行js脚本。在isolate中,所有结果都将异步返回,使用 `await` 来获取结果:
+
+```dart
+try {
+ print(await engine.evaluate(code ?? ''));
+} catch (e) {
+ print(e.toString());
+}
+```
+
+使用 `close` 方法销毁 isolate 线程,其在再次调用 `evaluate` 时将会重建。
+
+## 调用 Dart 函数
+
+Js脚本返回函数将被转换为 `JSInvokable`。 **它不能像 `Function` 一样调用,请使用 `invoke` 方法来调用**:
+
+```dart
+(func as JSInvokable).invoke([arg1, arg2], thisVal);
+```
+
+**注:** 返回 `JSInvokable` 可能造成引用泄漏,需要手动调用 `free` 来释放引用:
+
+```dart
+(obj as JSRef).free();
+// or JSRef.freeRecursive(obj);
+```
+
+传递给 `JSInvokable` 的参数将自动释放. 使用 `dup` 来保持引用:
+
+```dart
+(obj as JSRef).dup();
+// or JSRef.dupRecursive(obj);
+```
+
+自 v0.3.0 起,dart 函数可以作为参数传递给 `JSInvokable`,且 `channel` 函数不再默认内置。可以使用如下方法将 dart 函数赋值给全局,例如,使用 `Dio` 来为 qjs 提供 http 支持:
+
+```dart
+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();
+```
+
+在 isolate 模式下,只有顶层和静态函数能作为参数传给 `JSInvokable`,函数将在 isolate 线程中调用。 使用 `IsolateFunction` 来传递局部函数(将在主线程中调用):
+
+```dart
+await setToGlobalObject.invoke([
+ "http",
+ IsolateFunction((String url) {
+ return Dio().get(url).then((response) => response.data);
+ }),
+]);
+```
\ No newline at end of file
diff --git a/README.md b/README.md
index 8127946..5a7cc67 100644
--- a/README.md
+++ b/README.md
@@ -10,13 +10,15 @@


+[English](README.md) | [中文](README-CN.md)
+
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 dispatch event loop:
+Firstly, create a `FlutterQjs` object, then call `dispatch` to establish event loop:
```dart
final engine = FlutterQjs(
@@ -25,7 +27,7 @@ final engine = FlutterQjs(
engine.dispatch();
```
-Use `evaluate` method to run js script, now you can use it synchronously, or use await to resolve `Promise`:
+Use `evaluate` method to run js script, it runs synchronously, you can use await to resolve `Promise`:
```dart
try {
@@ -35,7 +37,7 @@ try {
}
```
-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.
+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**
```dart
try {
@@ -49,41 +51,21 @@ 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
JSInvokable | function(....args) |
-| Future | Promise |
-| JSError | Error |
-| Object | DartObject |
+| 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 |
-**notice:** `JSInvokable` does not extend `Function`, but can be used same as `Function`.
-Dart function uses named argument `thisVal` to manage js function `this`:
-
-```dart
-func(arg1, arg2, {thisVal});
-```
-
-or use `invoke` method to pass list parameters:
-
-```dart
-(func as JSInvokable).invoke([arg1, arg2], thisVal);
-```
-
-`JSInvokable` returned by evaluation may increase reference of JS object.
-You should manually call `free` to release JS reference:
-
-```dart
-(func as JSInvokable).free();
-```
-
-### Use modules
+## Use Modules
ES6 module with `import` function is supported and can be managed in dart with `moduleHandler`:
@@ -105,9 +87,8 @@ 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)
-
-### Run on isolate thread
+To use async function in module handler, try [run on isolate thread](#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:
@@ -121,7 +102,7 @@ final engine = IsolateQjs(
// not need engine.dispatch();
```
-Same as run on main thread, use `evaluate` to run js script. In this way, `Promise` return by `evaluate` will be automatically tracked and return the resolved data:
+Same as run on main thread, use `evaluate` to run js script. In isolate, everything returns asynchronously, use `await` to get the result:
```dart
try {
@@ -131,25 +112,49 @@ try {
}
```
-Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`.
+Method `close` can destroy isolate thread that will be recreated again if you call `evaluate`.
-**notice:** Make sure arguments passed to `IsolateJSFunction` are avaliable for isolate, such as primities and top level function.
-Method `bind` can help to pass instance function to isolate:
+## 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**:
```dart
-await jsFunc(await engine.bind(({thisVal}) {
- // DO SOMETHING
-}));
+(func as JSInvokable).invoke([arg1, arg2], thisVal);
```
-[This example](example/lib/main.dart) contains a complete demonstration on how to use this plugin.
-
-## Breaking change in v0.3.0
-
-`channel` function is no longer included by default.
-Use js function to set dart object globally:
+**notice:** evaluation returning `JSInvokable` may cause reference leak.
+You should manually call `free` to release JS reference.
```dart
-final setToGlobalObject = await engine.evaluate("(key, val) => this[key] = val;");
-await setToGlobalObject("channel", methodHandler);
+(obj as JSRef).free();
+// or JSRef.freeRecursive(obj);
+```
+
+Arguments passed into `JSInvokable` will be freed automatically. Use `dup` to keep the reference.
+
+```dart
+(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:
+
+```dart
+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:
+
+```dart
+await setToGlobalObject.invoke([
+ "http",
+ IsolateFunction((String url) {
+ return Dio().get(url).then((response) => response.data);
+ }),
+]);
```
\ No newline at end of file
diff --git a/cxx/ffi.cpp b/cxx/ffi.cpp
index 3e799da..12da8cf 100644
--- a/cxx/ffi.cpp
+++ b/cxx/ffi.cpp
@@ -15,7 +15,7 @@ extern "C"
DLLEXPORT JSValue *jsThrow(JSContext *ctx, JSValue *obj)
{
- return new JSValue(JS_Throw(ctx, *obj));
+ return new JSValue(JS_Throw(ctx, JS_DupValue(ctx, *obj)));
}
DLLEXPORT JSValue *jsEXCEPTION()
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 0566f0c..591231c 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -75,7 +75,7 @@ packages:
path: ".."
relative: true
source: path
- version: "0.3.2"
+ version: "0.3.3"
flutter_test:
dependency: "direct dev"
description: flutter
diff --git a/lib/flutter_qjs.dart b/lib/flutter_qjs.dart
index f99a917..be401cf 100644
--- a/lib/flutter_qjs.dart
+++ b/lib/flutter_qjs.dart
@@ -5,7 +5,7 @@ import 'dart:isolate';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'src/ffi.dart';
-export 'src/ffi.dart' show JSEvalFlag;
+export 'src/ffi.dart' show JSEvalFlag, JSRef;
part 'src/engine.dart';
part 'src/isolate.dart';
diff --git a/lib/src/ffi.dart b/lib/src/ffi.dart
index 1eec922..dade9a9 100644
--- a/lib/src/ffi.dart
+++ b/lib/src/ffi.dart
@@ -22,6 +22,35 @@ abstract class JSRef {
}
void destroy();
+
+ static void freeRecursive(dynamic obj) {
+ _callRecursive(obj, (ref) => ref.free());
+ }
+
+ static void dupRecursive(dynamic obj) {
+ _callRecursive(obj, (ref) => ref.dup());
+ }
+
+ static void _callRecursive(
+ dynamic obj,
+ void Function(JSRef) cb, [
+ Set cache,
+ ]) {
+ if (obj == null) return;
+ if (cache == null) cache = Set();
+ if (cache.contains(obj)) return;
+ if (obj is List) {
+ cache.add(obj);
+ obj.forEach((e) => _callRecursive(e, cb, cache));
+ }
+ if (obj is Map) {
+ cache.add(obj);
+ obj.values.forEach((e) => _callRecursive(e, cb, cache));
+ }
+ if (obj is JSRef) {
+ cb(obj);
+ }
+ }
}
abstract class JSRefLeakable {}
@@ -188,8 +217,10 @@ void jsFreeRuntime(
}
while (0 < runtimeOpaques[rt]?._ref?.length ?? 0) {
final ref = runtimeOpaques[rt]?._ref?.first;
+ final objStrs = ref.toString().split('\n');
+ final objStr = objStrs.length > 0 ? objStrs[0] + " ..." : objStrs[0];
referenceleak.add(
- " ${identityHashCode(ref)}\t${ref._refCount + 1}\t${ref.runtimeType.toString()}\t${ref.toString().replaceAll('\n', '\\n')}");
+ " ${identityHashCode(ref)}\t${ref._refCount + 1}\t${ref.runtimeType.toString()}\t$objStr");
ref.destroy();
}
_jsFreeRuntime(rt);
diff --git a/lib/src/isolate.dart b/lib/src/isolate.dart
index 42ee310..2fc4e1e 100644
--- a/lib/src/isolate.dart
+++ b/lib/src/isolate.dart
@@ -17,11 +17,7 @@ abstract class _IsolateEncodable {
Map _encode();
}
-final List _sendAllowType = [Null, String, int, double, bool, SendPort];
-
dynamic _encodeData(dynamic data, {Map cache}) {
- if (data is Function) return data;
- if (_sendAllowType.contains(data.runtimeType)) return data;
if (cache == null) cache = Map();
if (cache.containsKey(data)) return cache[data];
if (data is _IsolateEncodable) return data._encode();
@@ -59,7 +55,7 @@ dynamic _encodeData(dynamic data, {Map cache}) {
#jsFuturePort: futurePort.sendPort,
};
}
- throw JSError('unsupport type: ${data.runtimeType}\n${data.toString()}');
+ return data;
}
dynamic _decodeData(dynamic data, {Map cache}) {
diff --git a/lib/src/object.dart b/lib/src/object.dart
index 7fd93e9..73d1530 100644
--- a/lib/src/object.dart
+++ b/lib/src/object.dart
@@ -24,23 +24,6 @@ class _DartFunction extends JSInvokable {
final Function _func;
_DartFunction(this._func);
- void _freeRecursive(dynamic obj, [Set cache]) {
- if (obj == null) return;
- if (cache == null) cache = Set();
- if (cache.contains(obj)) return;
- if (obj is List) {
- cache.add(obj);
- obj.forEach((e) => _freeRecursive(e, cache));
- }
- if (obj is Map) {
- cache.add(obj);
- obj.values.forEach((e) => _freeRecursive(e, cache));
- }
- if (obj is JSRef) {
- obj.free();
- }
- }
-
@override
invoke(List args, [thisVal]) {
/// wrap this into function
@@ -48,8 +31,8 @@ class _DartFunction extends JSInvokable {
RegExp('{.*thisVal.*}').hasMatch(_func.runtimeType.toString());
final ret =
Function.apply(_func, args, passThis ? {#thisVal: thisVal} : null);
- _freeRecursive(args);
- _freeRecursive(thisVal);
+ JSRef.freeRecursive(args);
+ JSRef.freeRecursive(thisVal);
return ret;
}
@@ -179,7 +162,7 @@ class _JSFunction extends _JSObject implements JSInvokable, _IsolateEncodable {
}
Pointer _invoke(List arguments, [dynamic thisVal]) {
- if (_val == null) return null;
+ if (_val == null) throw JSError("InternalError: JSValue released");
List args = arguments
.map(
(e) => _dartToJs(_ctx, e),
@@ -196,22 +179,26 @@ class _JSFunction extends _JSObject implements JSInvokable, _IsolateEncodable {
@override
Map _encode() {
- final func = IsolateFunction._new(this);
- final ret = func._encode();
- return ret;
+ return IsolateFunction._new(this)._encode();
}
}
-abstract class _IsolatePortHandler {
+/// Dart function wrapper for isolate
+class IsolateFunction extends JSInvokable implements _IsolateEncodable {
int _isolateId;
- dynamic _handle(dynamic);
-}
+ SendPort _port;
+ JSInvokable _invokable;
+ IsolateFunction._fromId(this._isolateId, this._port);
+
+ IsolateFunction._new(this._invokable) {
+ _handlers.add(this);
+ }
+ IsolateFunction(Function func) : this._new(_DartFunction(func));
-class _IsolatePort {
static ReceivePort _invokeHandler;
- static Set<_IsolatePortHandler> _handlers = Set();
+ static Set _handlers = Set();
- static get _port {
+ static get _handlePort {
if (_invokeHandler == null) {
_invokeHandler = ReceivePort();
_invokeHandler.listen((msg) async {
@@ -236,11 +223,11 @@ class _IsolatePort {
return _invokeHandler.sendPort;
}
- static _send(SendPort isolate, _IsolatePortHandler handler, msg) async {
- if (isolate == null) return handler._handle(msg);
+ _send(msg) async {
+ if (_port == null) return _handle(msg);
final evaluatePort = ReceivePort();
- isolate.send({
- #handler: handler._isolateId,
+ _port.send({
+ #handler: _isolateId,
#msg: msg,
#port: evaluatePort.sendPort,
});
@@ -250,33 +237,11 @@ class _IsolatePort {
return _decodeData(result);
}
- static _add(_IsolatePortHandler sendport) => _handlers.add(sendport);
- static _remove(_IsolatePortHandler sendport) => _handlers.remove(sendport);
-}
-
-/// Dart function wrapper for isolate
-class IsolateFunction extends JSInvokable
- implements _IsolateEncodable, _IsolatePortHandler {
- @override
- int _isolateId;
- SendPort _port;
- JSInvokable _invokable;
- IsolateFunction._fromId(this._isolateId, this._port);
-
- IsolateFunction._new(this._invokable) {
- _IsolatePort._add(this);
- }
-
- static IsolateFunction func(Function func) {
- return IsolateFunction._new(_DartFunction(func));
- }
-
_destroy() {
- _IsolatePort._remove(this);
+ _handlers.remove(this);
_invokable?.free();
}
- @override
_handle(msg) async {
switch (msg) {
case #dup:
@@ -284,7 +249,6 @@ class IsolateFunction extends JSInvokable
return null;
case #free:
_refCount--;
- print("${identityHashCode(this)} ref $_refCount");
if (_refCount < 0) _destroy();
return null;
case #destroy:
@@ -300,8 +264,7 @@ class IsolateFunction extends JSInvokable
Future invoke(List positionalArguments, [thisVal]) async {
List dArgs = _encodeData(positionalArguments);
Map dThisVal = _encodeData(thisVal);
- return _IsolatePort._send(_port, this, {
- #type: #invokeIsolate,
+ return _send({
#args: dArgs,
#thisVal: dThisVal,
});
@@ -320,7 +283,7 @@ class IsolateFunction extends JSInvokable
Map _encode() {
return {
#jsFunctionId: _isolateId ?? identityHashCode(this),
- #jsFunctionPort: _port ?? _IsolatePort._port,
+ #jsFunctionPort: _port ?? IsolateFunction._handlePort,
};
}
@@ -328,16 +291,16 @@ class IsolateFunction extends JSInvokable
@override
dup() {
- _IsolatePort._send(_port, this, #dup);
+ _send(#dup);
}
@override
free() {
- _IsolatePort._send(_port, this, #free);
+ _send(#free);
}
@override
void destroy() {
- _IsolatePort._send(_port, this, #destroy);
+ _send(#destroy);
}
}
diff --git a/lib/src/wrapper.dart b/lib/src/wrapper.dart
index 083e63a..55e1e97 100644
--- a/lib/src/wrapper.dart
+++ b/lib/src/wrapper.dart
@@ -55,6 +55,8 @@ Pointer _jsGetPropertyValue(
Pointer _dartToJs(Pointer ctx, dynamic val, {Map cache}) {
if (val == null) return jsUNDEFINED();
+ if (val is Error) return _dartToJs(ctx, JSError(val, val.stackTrace));
+ if (val is Exception) return _dartToJs(ctx, JSError(val));
if (val is JSError) {
final ret = jsNewError(ctx);
_definePropertyValue(ctx, ret, "name", "");
@@ -187,9 +189,11 @@ dynamic _jsToDart(Pointer ctx, Pointer val, {Map cache}) {
final jsPromise = _JSObject(ctx, val);
final jsRet = promiseThen._invoke([
(v) {
+ JSRef.dupRecursive(v);
if (!completer.isCompleted) completer.complete(v);
},
(e) {
+ JSRef.dupRecursive(e);
if (!completer.isCompleted) completer.completeError(e);
},
], jsPromise);
diff --git a/pubspec.yaml b/pubspec.yaml
index c882ae1..461f92d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
name: flutter_qjs
description: This plugin is a simple js engine for flutter using the `quickjs` project. Plugin currently supports all the platforms except web!
-version: 0.3.2
+version: 0.3.3
homepage: https://github.com/ekibun/flutter_qjs
environment:
diff --git a/test/flutter_qjs_test.dart b/test/flutter_qjs_test.dart
index fc574fc..18036f0 100644
--- a/test/flutter_qjs_test.dart
+++ b/test/flutter_qjs_test.dart
@@ -8,7 +8,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
-import 'dart:isolate';
import 'package:flutter_qjs/flutter_qjs.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -19,7 +18,7 @@ dynamic myFunction(String args, {thisVal}) {
Future testEvaluate(qjs) async {
JSInvokable wrapFunction = await qjs.evaluate(
- '(a) => a',
+ 'async (a) => a',
name: '',
);
dynamic testWrap = await wrapFunction.invoke([wrapFunction]);
@@ -68,11 +67,6 @@ Future testEvaluate(qjs) async {
}
void main() async {
- test('send', () async {
- final rec = ReceivePort();
- rec.close();
- rec.sendPort.send("3232");
- });
test('make', () async {
final utf8Encoding = Encoding.getByName('utf-8');
var cmakePath = 'cmake';
@@ -139,25 +133,22 @@ void main() async {
await testEvaluate(qjs);
await qjs.close();
});
- test('isolate bind function', () async {
+ test('isolate bind this', () async {
final qjs = IsolateQjs();
- final localVars = [];
- JSInvokable testFunc =
- await qjs.evaluate('(func)=>func(()=>"ret")', name: '');
- final func = IsolateFunction.func((args) {
- localVars.add(args..dup());
+ JSInvokable localVar;
+ JSInvokable setToGlobal = await qjs
+ .evaluate('(name, func)=>{ this[name] = func }', name: '');
+ final func = IsolateFunction((args) {
+ localVar = args..dup();
return args.invoke([]);
});
- final testFuncRet = await testFunc.invoke([func..dup()]);
- final testFuncRet2 = await testFunc.invoke([func..dup()]);
+ await setToGlobal.invoke(["test", func..dup()]);
func.free();
- testFunc.free();
- for (IsolateFunction vars in localVars) {
- expect(await vars.invoke([]), 'ret', reason: 'bind function');
- vars.free();
- }
+ setToGlobal.free();
+ final testFuncRet = await qjs.evaluate('test(()=>"ret")', name: '');
+ expect(await localVar.invoke([]), 'ret', reason: 'bind function');
+ localVar.free();
expect(testFuncRet, 'ret', reason: 'bind function args return');
- expect(testFuncRet2, testFuncRet, reason: 'bind function args return2');
await qjs.close();
});
test('reference leak', () async {