diff --git a/CHANGELOG.md b/CHANGELOG.md
index 307ed5e..59bf684 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,12 @@
* @LastEditTime: 2020-12-02 11:36:40
-->
+## 0.2.0
+
+* breakdown change with new constructor.
+* fix make release in ios.
+* fix crash in wrapping js Promise.
+
## 0.1.4
* fix crash on android x86.
diff --git a/README.md b/README.md
index afa9418..81ea6b9 100644
--- a/README.md
+++ b/README.md
@@ -19,84 +19,79 @@ ES6 module with `import` function is supported and can be managed in dart with `
A global function `channel` is presented to invoke dart function. 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 |
-| JSFunction | function(....args) |
-| Future | Promise |
+| dart | js |
+| --------------------------------------------------- | ------------------ |
+| Bool | boolean |
+| Int | number |
+| Double | number |
+| String | string |
+| Uint8List | ArrayBuffer |
+| List | Array |
+| Map | Object |
+| JSFunction(...args)
IsolateJSFunction(...args) | function(....args) |
+| Future | Promise |
-**notice:** `function` can only be sent from js to dart. `Promise` return by `evaluate` will be automatically tracked and return the resolved data.
+**notice:** `function` can only be sent from js to dart. `IsolateJSFunction` always returns asynchronously.
## Getting Started
### Run on main thread
-1. Create a `FlutterQjs` object. Call `dispatch` to dispatch event loop.
+1. Create a `FlutterQjs` object, pass handlers to implement js-dart interaction and resolving modules. For example, you can use `Dio` to implement http in js:
```dart
-final engine = FlutterQjs();
-await engine.dispatch();
+final engine = FlutterQjs(
+ methodHandler: (String method, List arg) {
+ switch (method) {
+ case "http":
+ return Dio().get(arg[0]).then((response) => response.data);
+ default:
+ throw Exception("No such method");
+ }
+ },
+ moduleHandler: (String module) {
+ if(module == "hello")
+ return "export default (name) => `hello \${name}!`;";
+ throw Exception("Module Not found");
+ },
+);
```
-2. Call `setMethodHandler` to implement js-dart interaction. For example, you can use `Dio` to implement http in js:
-
-```dart
-await engine.setMethodHandler((String method, List arg) {
- switch (method) {
- case "http":
- return Dio().get(arg[0]).then((response) => response.data);
- default:
- throw Exception("No such method");
- }
-});
-```
-
-and in javascript, call `channel` function to get data, make sure the second parameter is a list:
+in javascript, `channel` function is equiped to invoke `methodHandler`, make sure the second parameter is a list:
```javascript
channel("http", ["http://example.com/"]);
```
-3. Call `setModuleHandler` to resolve the js module.
-
-~~I cannot find a way to convert the sync ffi callback into an async function. So the assets files received by async function `rootBundle.loadString` cannot be used in this version. I will appreciate it if you can provide me a solution to make `ModuleHandler` async.~~
-
-To use async function in module handler, try [Run on isolate thread](#Run-on-isolate-thread)
-
-```dart
-await engine.setModuleHandler((String module) {
- if(module == "hello") return "export default (name) => `hello \${name}!`;";
- throw Exception("Module Not found");
-});
-```
-
-and in javascript, call `import` function to get module:
+`import` function is used to get modules:
```javascript
import("hello").then(({default: greet}) => greet("world"));
```
-4. Use `evaluate` to run js script:
+**notice:** To use async function in module handler, try [Run on isolate thread](#Run-on-isolate-thread)
+
+2. Then call `dispatch` to dispatch event loop.
+
+```dart
+engine.dispatch();
+```
+
+1. Use `evaluate` to run js script, now you can use it synchronously, or use await to resolve `Promise`:
```dart
try {
- print(await engine.evaluate(code ?? '', ""));
+ print(engine.evaluate(code ?? ''));
} catch (e) {
print(e.toString());
}
```
-5. Method `recreate` can destroy quickjs runtime that can be recreated again if you call `evaluate`, `recreat` can be used to reset the module cache. Call `close` to stop `dispatch` when you do not need it.
+1. Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`.
### Run on isolate thread
-1. Create a `IsolateQjs` object, pass a handler to implement js-dart interaction. The handler is used in isolate, so the function must be a top-level function or a static method.
+1. Create a `IsolateQjs` object, pass handlers to implement js-dart interaction and resolving modules. The `methodHandler` is used in isolate, so **the handler function must be a top-level function or a static method**. Async function such as `rootBundle.loadString` can be used now to get module:
```dart
dynamic methodHandler(String method, List arg) {
@@ -107,32 +102,17 @@ dynamic methodHandler(String method, List arg) {
throw Exception("No such method");
}
}
-final engine = IsolateQjs(methodHandler);
+final engine = IsolateQjs(
+ methodHandler: methodHandler,
+ moduleHandler: (String module) async {
+ return await rootBundle.loadString(
+ "js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
+ },
+);
// not need engine.dispatch();
```
-and in javascript, call `channel` function to get data, make sure the second parameter is a list:
-
-```javascript
-channel("http", ["http://example.com/"]);
-```
-
-2. Call `setModuleHandler` to resolve the js module. Async function such as `rootBundle.loadString` can be used now to get module. The handler is called in main thread.
-
-```dart
-await engine.setModuleHandler((String module) async {
- return await rootBundle.loadString(
- "js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
-});
-```
-
-and in javascript, call `import` function to get module:
-
-```javascript
-import("hello").then(({default: greet}) => greet("world"));
-```
-
-3. Same as run on main thread, use `evaluate` to run js script:
+2. 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:
```dart
try {
@@ -142,7 +122,7 @@ try {
}
```
-4. Method `close` (same as `recreate` in main thread) can destroy quickjs runtime that can be recreated again if you call `evaluate`.
+3. Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`.
[This example](example/lib/main.dart) contains a complete demonstration on how to use this plugin.
diff --git a/cxx/ffi.cpp b/cxx/ffi.cpp
index 5b8c186..e3dc7ba 100644
--- a/cxx/ffi.cpp
+++ b/cxx/ffi.cpp
@@ -154,16 +154,18 @@ extern "C"
return new JSValue(JS_NewObject(ctx));
}
- DLLEXPORT void jsFreeValue(JSContext *ctx, JSValue *v)
+ DLLEXPORT void jsFreeValue(JSContext *ctx, JSValue *v, int32_t free)
{
JS_FreeValue(ctx, *v);
- delete v;
+ if (free)
+ delete v;
}
- DLLEXPORT void jsFreeValueRT(JSRuntime *rt, JSValue *v)
+ DLLEXPORT void jsFreeValueRT(JSRuntime *rt, JSValue *v, int32_t free)
{
JS_FreeValueRT(rt, *v);
- delete v;
+ if (free)
+ delete v;
}
DLLEXPORT JSValue *jsDupValue(JSContext *ctx, JSValueConst *v)
@@ -215,6 +217,11 @@ extern "C"
return JS_IsFunction(ctx, *val);
}
+ DLLEXPORT int32_t jsIsPromise(JSContext *ctx, JSValueConst *val)
+ {
+ return JS_IsPromise(ctx, *val);
+ }
+
DLLEXPORT int32_t jsIsArray(JSContext *ctx, JSValueConst *val)
{
return JS_IsArray(ctx, *val);
diff --git a/cxx/ffi.h b/cxx/ffi.h
index 7f2b299..be25049 100644
--- a/cxx/ffi.h
+++ b/cxx/ffi.h
@@ -51,9 +51,9 @@ extern "C"
DLLEXPORT JSValue *jsNewObject(JSContext *ctx);
- DLLEXPORT void jsFreeValue(JSContext *ctx, JSValue *v);
+ DLLEXPORT void jsFreeValue(JSContext *ctx, JSValue *v, int32_t free);
- DLLEXPORT void jsFreeValueRT(JSRuntime *rt, JSValue *v);
+ DLLEXPORT void jsFreeValueRT(JSRuntime *rt, JSValue *v, int32_t free);
DLLEXPORT JSValue *jsDupValue(JSContext *ctx, JSValueConst *v);
@@ -73,6 +73,8 @@ extern "C"
DLLEXPORT int32_t jsIsFunction(JSContext *ctx, JSValueConst *val);
+ DLLEXPORT int32_t jsIsPromise(JSContext *ctx, JSValueConst *val);
+
DLLEXPORT int32_t jsIsArray(JSContext *ctx, JSValueConst *val);
DLLEXPORT JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
diff --git a/cxx/quickjs b/cxx/quickjs
index 5143636..fc5dca5 160000
--- a/cxx/quickjs
+++ b/cxx/quickjs
@@ -1 +1 @@
-Subproject commit 5143636b2db8183cd45e6143d3e7d32d182e78d8
+Subproject commit fc5dca513b3b6a5e1cbe9da24fbd8491887bdc0d
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 3717589..269c9af 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -75,12 +75,14 @@ class _TestPageState extends State {
_ensureEngine() {
if (engine != null) return;
- engine = IsolateQjs(methodHandler);
- engine.setModuleHandler((String module) async {
- if (module == "test") return "export default '${new DateTime.now()}'";
- return await rootBundle.loadString(
- "js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
- });
+ engine = IsolateQjs(
+ methodHandler: methodHandler,
+ moduleHandler: (String module) async {
+ if (module == "test") return "export default '${new DateTime.now()}'";
+ return await rootBundle.loadString(
+ "js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
+ },
+ );
}
@override
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 3b5e630..fc2018c 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -82,7 +82,7 @@ packages:
path: ".."
relative: true
source: path
- version: "0.1.4"
+ version: "0.2.0"
flutter_test:
dependency: "direct dev"
description: flutter
diff --git a/lib/ffi.dart b/lib/ffi.dart
index 1968d18..f606297 100644
--- a/lib/ffi.dart
+++ b/lib/ffi.dart
@@ -16,7 +16,7 @@ abstract class JSRef {
}
/// JS_Eval() flags
-class JSEvalType {
+class JSEvalFlag {
static const GLOBAL = 0 << 0;
static const MODULE = 1 << 0;
}
@@ -110,9 +110,9 @@ typedef JSChannel = Pointer Function(Pointer ctx, Pointer method, Pointer argv);
class RuntimeOpaque {
JSChannel channel;
- List ref = List();
+ List ref = [];
ReceivePort port;
- Future Function(Pointer) promsieToFuture;
+ Future Function(Pointer) promiseToFuture;
}
final Map runtimeOpaques = Map();
@@ -356,32 +356,52 @@ final Pointer Function(
)>>("jsNewObject")
.asFunction();
-/// void jsFreeValue(JSContext *ctx, JSValue *val)
+/// void jsFreeValue(JSContext *ctx, JSValue *val, int32_t free)
final void Function(
Pointer ctx,
Pointer val,
-) jsFreeValue = qjsLib
+ int free,
+) _jsFreeValue = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
Pointer,
+ Int32,
)>>("jsFreeValue")
.asFunction();
-/// void jsFreeValueRT(JSRuntime *rt, JSValue *v)
+void jsFreeValue(
+ Pointer ctx,
+ Pointer val, {
+ bool free = true,
+}) {
+ _jsFreeValue(ctx, val, free ? 1 : 0);
+}
+
+/// void jsFreeValue(JSRuntime *rt, JSValue *val, int32_t free)
final void Function(
Pointer rt,
Pointer val,
-) jsFreeValueRT = qjsLib
+ int free,
+) _jsFreeValueRT = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
Pointer,
+ Int32,
)>>("jsFreeValueRT")
.asFunction();
+void jsFreeValueRT(
+ Pointer rt,
+ Pointer val, {
+ bool free = true,
+}) {
+ _jsFreeValueRT(rt, val, free ? 1 : 0);
+}
+
/// JSValue *jsDupValue(JSContext *ctx, JSValueConst *v)
final Pointer Function(
Pointer ctx,
@@ -512,6 +532,19 @@ final int Function(
)>>("jsIsFunction")
.asFunction();
+/// int32_t jsIsPromise(JSContext *ctx, JSValueConst *val)
+final int Function(
+ Pointer ctx,
+ Pointer val,
+) jsIsPromise = qjsLib
+ .lookup<
+ NativeFunction<
+ Int32 Function(
+ Pointer,
+ Pointer,
+ )>>("jsIsPromise")
+ .asFunction();
+
/// int32_t jsIsArray(JSContext *ctx, JSValueConst *val)
final int Function(
Pointer ctx,
diff --git a/lib/flutter_qjs.dart b/lib/flutter_qjs.dart
index 743f0c1..2d79227 100644
--- a/lib/flutter_qjs.dart
+++ b/lib/flutter_qjs.dart
@@ -23,9 +23,15 @@ class FlutterQjs {
Pointer _rt;
Pointer _ctx;
ReceivePort port = ReceivePort();
+
+ /// Set a handler to manage js call with `channel(method, args)` function.
JsMethodHandler methodHandler;
+
+ /// Set a handler to manage js module.
JsModuleHandler moduleHandler;
+ FlutterQjs({this.methodHandler, this.moduleHandler});
+
_ensureEngine() {
if (_rt != null) return;
_rt = jsNewRuntime((ctx, method, argv) {
@@ -61,18 +67,8 @@ class FlutterQjs {
_ctx = jsNewContextWithPromsieWrapper(_rt);
}
- /// Set a handler to manage js call with `channel(method, args)` function.
- setMethodHandler(JsMethodHandler handler) {
- methodHandler = handler;
- }
-
- /// Set a handler to manage js module.
- setModuleHandler(JsModuleHandler handler) {
- moduleHandler = handler;
- }
-
/// Free Runtime and Context which can be recreate when evaluate again.
- recreate() {
+ close() {
if (_rt != null) {
jsFreeContext(_ctx);
jsFreeRuntime(_rt);
@@ -81,18 +77,10 @@ class FlutterQjs {
_ctx = null;
}
- /// Close ReceivePort.
- close() {
- if (port != null) {
- port.close();
- recreate();
- }
- port = null;
- }
-
/// DispatchMessage
Future dispatch() async {
await for (var _ in port) {
+ if (_rt == null) continue;
while (true) {
int err = jsExecutePendingJob(_rt);
if (err <= 0) {
@@ -116,24 +104,14 @@ class FlutterQjs {
}
/// Evaluate js script.
- Future evaluate(String command, {String name, int evalFlags}) async {
+ dynamic evaluate(String command, {String name, int evalFlags}) {
_ensureEngine();
- var jsval =
- jsEval(_ctx, command, name ?? "", evalFlags ?? JSEvalType.GLOBAL);
- if (jsIsException(jsval) != 0) {
- jsFreeValue(_ctx, jsval);
- throw Exception(parseJSException(_ctx));
- }
- var ret = runtimeOpaques[_rt]?.promsieToFuture(jsval);
- jsFreeValue(_ctx, jsval);
- return ret;
- }
-
- /// Evaluate js script (Sync).
- dynamic evaluateSync(String command, {String name, int evalFlags}) {
- _ensureEngine();
- var jsval =
- jsEval(_ctx, command, name ?? "", evalFlags ?? JSEvalType.GLOBAL);
+ var jsval = jsEval(
+ _ctx,
+ command,
+ name ?? "",
+ evalFlags ?? JSEvalFlag.GLOBAL,
+ );
if (jsIsException(jsval) != 0) {
jsFreeValue(_ctx, jsval);
throw Exception(parseJSException(_ctx));
diff --git a/lib/isolate.dart b/lib/isolate.dart
index 80099cb..7b4b8aa 100644
--- a/lib/isolate.dart
+++ b/lib/isolate.dart
@@ -76,8 +76,20 @@ dynamic _encodeData(dynamic data, {Map cache}) {
};
}
if (data is Future) {
- // Not support
- return {};
+ var futurePort = ReceivePort();
+ data.then((value) {
+ futurePort.first.then((port) => {
+ (port as SendPort).send({'data': _encodeData(value)})
+ });
+ }, onError: (e, stack) {
+ futurePort.first.then((port) => {
+ (port as SendPort)
+ .send({'error': e.toString() + "\n" + stack.toString()})
+ });
+ });
+ return {
+ '__js_future_port': futurePort.sendPort,
+ };
}
return data;
}
@@ -104,6 +116,20 @@ dynamic _decodeData(dynamic data, SendPort port,
return JSFunction.fromAddress(ctx, val);
}
}
+ if (data.containsKey('__js_future_port')) {
+ SendPort port = data['__js_future_port'];
+ var futurePort = ReceivePort();
+ port.send(futurePort.sendPort);
+ var futureCompleter = Completer();
+ futurePort.first.then((value) {
+ if (value['error'] != null) {
+ futureCompleter.completeError(value['error']);
+ } else {
+ futureCompleter.complete(value['data']);
+ }
+ });
+ return futureCompleter.future;
+ }
var ret = {};
cache[data] = ret;
for (var entry in data.entries) {
@@ -116,30 +142,31 @@ dynamic _decodeData(dynamic data, SendPort port,
}
void _runJsIsolate(Map spawnMessage) async {
- var qjs = FlutterQjs();
SendPort sendPort = spawnMessage['port'];
JsMethodHandler methodHandler = spawnMessage['handler'];
ReceivePort port = ReceivePort();
sendPort.send(port.sendPort);
- qjs.setMethodHandler(methodHandler);
- qjs.setModuleHandler((name) {
- var ptr = allocate>();
- ptr.value = Pointer.fromAddress(0);
- sendPort.send({
- 'type': 'module',
- 'name': name,
- 'ptr': ptr.address,
- });
- while (ptr.value.address == 0) sleep(Duration.zero);
- if (ptr.value.address == -1) throw Exception("Module Not found");
- var ret = Utf8.fromUtf8(ptr.value);
- sendPort.send({
- 'type': 'release',
- 'ptr': ptr.value.address,
- });
- free(ptr);
- return ret;
- });
+ var qjs = FlutterQjs(
+ methodHandler: methodHandler,
+ moduleHandler: (name) {
+ var ptr = allocate>();
+ ptr.value = Pointer.fromAddress(0);
+ sendPort.send({
+ 'type': 'module',
+ 'name': name,
+ 'ptr': ptr.address,
+ });
+ while (ptr.value.address == 0) sleep(Duration.zero);
+ if (ptr.value.address == -1) throw Exception("Module Not found");
+ var ret = Utf8.fromUtf8(ptr.value);
+ sendPort.send({
+ 'type': 'release',
+ 'ptr': ptr.value.address,
+ });
+ free(ptr);
+ return ret;
+ },
+ );
qjs.dispatch();
await for (var msg in port) {
var data;
@@ -160,6 +187,7 @@ void _runJsIsolate(Map spawnMessage) async {
).invoke(_decodeData(msg['args'], null));
break;
case 'close':
+ qjs.port.close();
qjs.close();
port.close();
break;
@@ -182,12 +210,15 @@ typedef JsIsolateSpawn = void Function(SendPort sendPort);
class IsolateQjs {
Future _sendPort;
- JsMethodHandler _methodHandler;
- JsAsyncModuleHandler _moduleHandler;
/// Set a handler to manage js call with `channel(method, args)` function.
/// The function must be a top-level function or a static method
- IsolateQjs(this._methodHandler);
+ JsMethodHandler methodHandler;
+
+ /// Set a handler to manage js module.
+ JsAsyncModuleHandler moduleHandler;
+
+ IsolateQjs({this.methodHandler, this.moduleHandler});
_ensureEngine() {
if (_sendPort != null) return;
@@ -196,7 +227,7 @@ class IsolateQjs {
_runJsIsolate,
{
'port': port.sendPort,
- 'handler': _methodHandler,
+ 'handler': methodHandler,
},
errorsAreFatal: true,
);
@@ -210,7 +241,7 @@ class IsolateQjs {
case 'module':
var ptr = Pointer.fromAddress(msg['ptr']);
try {
- ptr.value = Utf8.toUtf8(await _moduleHandler(msg['name']));
+ ptr.value = Utf8.toUtf8(await moduleHandler(msg['name']));
} catch (e) {
ptr.value = Pointer.fromAddress(-1);
}
@@ -226,11 +257,6 @@ class IsolateQjs {
_sendPort = completer.future;
}
- /// Set a handler to manage js module.
- setModuleHandler(JsAsyncModuleHandler handler) {
- _moduleHandler = handler;
- }
-
close() {
if (_sendPort == null) return;
_sendPort.then((sendPort) {
@@ -253,8 +279,9 @@ class IsolateQjs {
'port': evaluatePort.sendPort,
});
var result = await evaluatePort.first;
- if (result['error'] == null)
+ if (result['error'] == null){
return _decodeData(result['data'], sendPort);
+ }
else
throw result['error'];
}
diff --git a/lib/wrapper.dart b/lib/wrapper.dart
index df92656..d9b8ff8 100644
--- a/lib/wrapper.dart
+++ b/lib/wrapper.dart
@@ -130,8 +130,8 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map cache}) {
var ret = jsNewPromiseCapability(ctx, resolvingFunc);
var res = jsToDart(ctx, resolvingFunc);
var rej = jsToDart(ctx, resolvingFunc2);
- jsFreeValue(ctx, resolvingFunc);
- jsFreeValue(ctx, resolvingFunc2);
+ jsFreeValue(ctx, resolvingFunc, free: false);
+ jsFreeValue(ctx, resolvingFunc2, free: false);
free(resolvingFunc);
val.then((value) {
res(value);
@@ -225,10 +225,12 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map cache}) {
}
if (jsIsFunction(ctx, val) != 0) {
return JSFunction(ctx, val);
+ } else if (jsIsPromise(ctx, val) != 0) {
+ return runtimeOpaques[jsGetRuntime(ctx)]?.promiseToFuture(val);
} else if (jsIsArray(ctx, val) != 0) {
Pointer jslength = jsGetPropertyStr(ctx, val, "length");
int length = jsToInt64(ctx, jslength);
- List ret = List();
+ List ret = [];
cache[valptr] = ret;
for (int i = 0; i < length; ++i) {
var jsAtomVal = jsNewInt64(ctx, i);
@@ -274,7 +276,8 @@ Pointer jsNewContextWithPromsieWrapper(Pointer rt) {
ctx,
"""
(value) => {
- const __ret = Promise.resolve(value)
+ const __ret = {};
+ Promise.resolve(value)
.then(v => {
__ret.__value = v;
__ret.__resolved = true;
@@ -286,10 +289,10 @@ Pointer jsNewContextWithPromsieWrapper(Pointer rt) {
}
""",
"",
- JSEvalType.GLOBAL);
+ JSEvalFlag.GLOBAL);
var promiseWrapper = JSRefValue(ctx, jsPromiseWrapper);
jsFreeValue(ctx, jsPromiseWrapper);
- runtimeOpaques[rt].promsieToFuture = (promise) {
+ runtimeOpaques[rt].promiseToFuture = (promise) {
var completer = Completer();
var wrapper = promiseWrapper.val;
if (wrapper == null)
diff --git a/pubspec.yaml b/pubspec.yaml
index aaa237b..ec291fe 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.1.4
+version: 0.2.0
homepage: https://github.com/ekibun/flutter_qjs
environment:
diff --git a/test/flutter_qjs_test.dart b/test/flutter_qjs_test.dart
index 48eff8c..6ba709b 100644
--- a/test/flutter_qjs_test.dart
+++ b/test/flutter_qjs_test.dart
@@ -5,9 +5,11 @@
* @LastEditors: ekibun
* @LastEditTime: 2020-10-07 00:11:27
*/
+import 'dart:async';
import 'dart:convert';
import 'dart:io';
+import 'package:flutter_qjs/ffi.dart';
import 'package:flutter_qjs/flutter_qjs.dart';
import 'package:flutter_qjs/isolate.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -16,6 +18,30 @@ dynamic myMethodHandler(method, args) {
return args;
}
+Future testEvaluate(qjs) async {
+ var value = await qjs.evaluate("""
+ const a = {};
+ a.a = a;
+ import('test').then((module) => channel('channel', [
+ (...args)=>`hello \${args}!`, a,
+ Promise.reject('test Promise.reject'), Promise.resolve('test Promise.resolve'),
+ 0.1, true, false, 1, "world", module
+ ]));
+ """, name: "");
+ expect(await value[0]('world'), 'hello world!', reason: "js function call");
+ expect(value[1]['a'], value[1], reason: "recursive object");
+ expect(value[2], isInstanceOf(), reason: "promise object");
+ try {
+ await value[2];
+ throw 'Future not reject';
+ } catch (e) {
+ expect(e, startsWith('test Promise.reject\n'),
+ reason: "promise object reject");
+ }
+ expect(await value[3], 'test Promise.resolve',
+ reason: "promise object resolve");
+}
+
void main() async {
test('make.windows', () async {
final utf8Encoding = Encoding.getByName('utf-8');
@@ -54,27 +80,56 @@ void main() async {
stderr.write(result.stderr);
expect(result.exitCode, 0);
}, testOn: 'mac-os');
- test('jsToDart', () async {
- final qjs = IsolateQjs(myMethodHandler);
- qjs.setModuleHandler((name) async {
- return "export default '${new DateTime.now()}'";
- });
- var value = await qjs.evaluate("""
- const a = {};
- a.a = a;
- import("test").then((module) => channel('channel', [
- (...args)=>`hello \${args}!`, a,
- 0.1, true, false, 1, "world", module
- ]));
- """, name: "");
- expect(value[1]['a'], value[1], reason: "recursive object");
- expect(await value[0]('world'), 'hello world!', reason: "js function call");
+ test('module', () async {
+ final qjs = FlutterQjs(
+ moduleHandler: (name) {
+ return "export default 'test module'";
+ },
+ );
+ qjs.dispatch();
+ qjs.evaluate('''
+ import handlerData from 'test';
+ export default {
+ data: handlerData
+ };
+ ''', name: 'evalModule', evalFlags: JSEvalFlag.MODULE);
+ var result = await qjs.evaluate('import("evalModule")');
+ expect(result['default']['data'], 'test module', reason: "eval module");
qjs.close();
});
+ test('jsToDart', () async {
+ await runZonedGuarded(() async {
+ final qjs = FlutterQjs(
+ methodHandler: myMethodHandler,
+ moduleHandler: (name) {
+ return "export default '${new DateTime.now()}'";
+ },
+ );
+ qjs.dispatch();
+ await testEvaluate(qjs);
+ qjs.close();
+ }, (e, stack) {
+ if (e is TestFailure) throw e;
+ });
+ });
+ test('isolate', () async {
+ await runZonedGuarded(() async {
+ final qjs = IsolateQjs(
+ methodHandler: myMethodHandler,
+ moduleHandler: (name) async {
+ return "export default '${new DateTime.now()}'";
+ },
+ );
+ await testEvaluate(qjs);
+ qjs.close();
+ }, (e, stack) {
+ if (e is TestFailure) throw e;
+ });
+ });
test('stack overflow', () async {
final qjs = FlutterQjs();
try {
- await qjs.evaluate("a=()=>a();a();", name: "");
+ qjs.evaluate("a=()=>a();a();", name: "");
} catch (e) {
expect(
e.toString(), startsWith('Exception: InternalError: stack overflow'),