diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b386bd..7941b38 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,11 @@
* @LastEditTime: 2020-12-02 11:36:40
-->
+## 0.3.1
+
+* code clean up.
+* fix isolate wrap error.
+
## 0.3.0
* breakdown change to remove `channel`.
diff --git a/README.md b/README.md
index 8b15947..1a1797e 100644
--- a/README.md
+++ b/README.md
@@ -45,20 +45,31 @@ 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 | function(....args) |
-| Future | Promise |
-| Object | DartObject |
+| dart | js |
+| ----------------------- | ------------------ |
+| Bool | boolean |
+| Int | number |
+| Double | number |
+| String | string |
+| Uint8List | ArrayBuffer |
+| List | Array |
+| Map | Object |
+| Function
JSInvokable | function(....args) |
+| Future | Promise |
+| Object | DartObject |
-**notice:** Dart function parameter `thisVal` is used to store `this` in js.
+**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);
+```
### Use modules
@@ -119,10 +130,11 @@ try {
Method `close` can destroy quickjs runtime that can 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:
+**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:
```dart
-await setToGlobalObject("func", await engine.bind(() {
+await setToGlobalObject("func", await engine.bind(({thisVal}) {
// DO SOMETHING
}))
```
@@ -131,8 +143,8 @@ await setToGlobalObject("func", await engine.bind(() {
## Breaking change in v0.3.0
-`channel` function is no longer utilized by default.
-Use js function to set to global:
+`channel` function is no longer included by default.
+Use js function to set dart object globally:
```dart
final setToGlobalObject = await engine.evaluate("(key, val) => this[key] = val;");
diff --git a/example/README.md b/example/README.md
index 2649690..595d78a 100644
--- a/example/README.md
+++ b/example/README.md
@@ -1,16 +1,3 @@
# flutter_qjs_example
Demonstrates how to use the flutter_qjs plugin.
-
-## 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://flutter.dev/docs/get-started/codelab)
-- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
-
-For help getting started with Flutter, view our
-[online documentation](https://flutter.dev/docs), which offers tutorials,
-samples, guidance on mobile development, and a full API reference.
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 711f95e..e270b7a 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -6,9 +6,7 @@
* @LastEditTime: 2020-12-02 11:28:06
*/
import 'package:flutter/material.dart';
-import 'dart:typed_data';
-import 'package:dio/dio.dart';
import 'package:flutter/services.dart';
import 'package:flutter_qjs/isolate.dart';
@@ -44,28 +42,6 @@ class TestPage extends StatefulWidget {
State createState() => _TestPageState();
}
-dynamic methodHandler(String method, List arg) {
- switch (method) {
- case "http":
- return Dio().get(arg[0]).then((response) => response.data);
- case "test":
- return arg[0]([
- true,
- 1,
- 0.5,
- "str",
- {"key": "val", 0: 1},
- Uint8List(2),
- Int32List(2),
- Int64List(2),
- Float64List(2),
- Float32List(2)
- ]);
- default:
- throw Exception("No such method");
- }
-}
-
class _TestPageState extends State {
String resp;
IsolateQjs engine;
@@ -82,9 +58,6 @@ class _TestPageState extends State {
"js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
},
);
- final setToGlobalObject =
- await engine.evaluate("(key, val) => this[key] = val;");
- setToGlobalObject("channel", methodHandler);
}
@override
diff --git a/example/pubspec.lock b/example/pubspec.lock
index 7ae5e34..2e79c76 100644
--- a/example/pubspec.lock
+++ b/example/pubspec.lock
@@ -43,13 +43,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.15.0-nullsafety.5"
- dio:
- dependency: "direct main"
- description:
- name: dio
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "3.0.10"
fake_async:
dependency: transitive
description:
@@ -82,7 +75,7 @@ packages:
path: ".."
relative: true
source: path
- version: "0.3.0"
+ version: "0.3.1"
flutter_test:
dependency: "direct dev"
description: flutter
@@ -95,13 +88,6 @@ packages:
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.6.0"
- http_parser:
- dependency: transitive
- description:
- name: http_parser
- url: "https://pub.flutter-io.cn"
- source: hosted
- version: "3.1.4"
matcher:
dependency: transitive
description:
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index bb430a3..2296396 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -22,7 +22,6 @@ dependencies:
highlight: 0.6.0
flutter_highlight: 0.6.0
- dio: 3.0.10
dev_dependencies:
flutter_test:
diff --git a/lib/ffi.dart b/lib/ffi.dart
index 855afd9..3a4d9bd 100644
--- a/lib/ffi.dart
+++ b/lib/ffi.dart
@@ -15,7 +15,6 @@ abstract class JSRef {
void release();
}
-/// JS_Eval() flags
class JSEvalFlag {
static const GLOBAL = 0 << 0;
static const MODULE = 1 << 0;
@@ -56,7 +55,7 @@ class JSTag {
static const FLOAT64 = 7;
}
-final DynamicLibrary qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
+final DynamicLibrary _qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
? (Platform.isWindows
? DynamicLibrary.open("test/build/Debug/ffiquickjs.dll")
: Platform.isMacOS
@@ -72,7 +71,7 @@ final DynamicLibrary qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
final Pointer Function(
Pointer ctx,
Pointer message,
-) _jsThrowInternalError = qjsLib
+) _jsThrowInternalError = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -89,12 +88,12 @@ Pointer jsThrowInternalError(Pointer ctx, String message) {
}
/// JSValue *jsEXCEPTION()
-final Pointer Function() jsEXCEPTION = qjsLib
+final Pointer Function() jsEXCEPTION = _qjsLib
.lookup>("jsEXCEPTION")
.asFunction();
/// JSValue *jsUNDEFINED()
-final Pointer Function() jsUNDEFINED = qjsLib
+final Pointer Function() jsUNDEFINED = _qjsLib
.lookup>("jsUNDEFINED")
.asFunction();
@@ -105,7 +104,7 @@ typedef JSChannelNative = Pointer Function(
/// JSRuntime *jsNewRuntime(JSChannel channel)
final Pointer Function(
Pointer>,
-) _jsNewRuntime = qjsLib
+) _jsNewRuntime = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -117,7 +116,6 @@ class RuntimeOpaque {
JSChannel channel;
List ref = [];
ReceivePort port;
- Future Function(Pointer) promiseToFuture;
int dartObjectClassId;
}
@@ -143,7 +141,7 @@ Pointer jsNewRuntime(
final void Function(
Pointer,
int,
-) jsSetMaxStackSize = qjsLib
+) jsSetMaxStackSize = _qjsLib
.lookup<
NativeFunction<
Void Function(
@@ -155,7 +153,7 @@ final void Function(
/// void jsFreeRuntime(JSRuntime *rt)
final void Function(
Pointer,
-) _jsFreeRuntime = qjsLib
+) _jsFreeRuntime = _qjsLib
.lookup<
NativeFunction<
Void Function(
@@ -177,7 +175,7 @@ void jsFreeRuntime(
final Pointer Function(
Pointer ctx,
Pointer funcData,
-) jsNewCFunction = qjsLib
+) jsNewCFunction = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -189,7 +187,7 @@ final Pointer Function(
/// JSContext *jsNewContext(JSRuntime *rt)
final Pointer Function(
Pointer rt,
-) jsNewContext = qjsLib
+) _jsNewContext = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -197,10 +195,18 @@ final Pointer Function(
)>>("jsNewContext")
.asFunction();
+Pointer jsNewContext(Pointer rt) {
+ var ctx = _jsNewContext(rt);
+ final runtimeOpaque = runtimeOpaques[rt];
+ if (runtimeOpaque == null) throw Exception("Runtime has been released!");
+ runtimeOpaque.dartObjectClassId = jsNewClass(ctx, "DartObject");
+ return ctx;
+}
+
/// void jsFreeContext(JSContext *ctx)
final void Function(
Pointer,
-) jsFreeContext = qjsLib
+) jsFreeContext = _qjsLib
.lookup<
NativeFunction<
Void Function(
@@ -211,7 +217,7 @@ final void Function(
/// JSRuntime *jsGetRuntime(JSContext *ctx)
final Pointer Function(
Pointer,
-) jsGetRuntime = qjsLib
+) jsGetRuntime = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -226,7 +232,7 @@ final Pointer Function(
int inputLen,
Pointer filename,
int evalFlags,
-) _jsEval = qjsLib
+) _jsEval = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -262,7 +268,7 @@ Pointer jsEval(
/// DLLEXPORT int32_t jsValueGetTag(JSValue *val)
final int Function(
Pointer val,
-) jsValueGetTag = qjsLib
+) jsValueGetTag = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -273,7 +279,7 @@ final int Function(
/// void *jsValueGetPtr(JSValue *val)
final Pointer Function(
Pointer val,
-) jsValueGetPtr = qjsLib
+) jsValueGetPtr = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -284,7 +290,7 @@ final Pointer Function(
/// DLLEXPORT bool jsTagIsFloat64(int32_t tag)
final int Function(
int val,
-) jsTagIsFloat64 = qjsLib
+) jsTagIsFloat64 = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -296,7 +302,7 @@ final int Function(
final Pointer Function(
Pointer ctx,
int val,
-) jsNewBool = qjsLib
+) jsNewBool = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -309,7 +315,7 @@ final Pointer Function(
final Pointer Function(
Pointer ctx,
int val,
-) jsNewInt64 = qjsLib
+) jsNewInt64 = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -322,7 +328,7 @@ final Pointer Function(
final Pointer Function(
Pointer ctx,
double val,
-) jsNewFloat64 = qjsLib
+) jsNewFloat64 = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -335,7 +341,7 @@ final Pointer Function(
final Pointer Function(
Pointer ctx,
Pointer str,
-) _jsNewString = qjsLib
+) _jsNewString = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -357,7 +363,7 @@ final Pointer Function(
Pointer ctx,
Pointer buf,
int len,
-) jsNewArrayBufferCopy = qjsLib
+) jsNewArrayBufferCopy = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -370,7 +376,7 @@ final Pointer Function(
/// JSValue *jsNewArray(JSContext *ctx)
final Pointer Function(
Pointer ctx,
-) jsNewArray = qjsLib
+) jsNewArray = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -381,7 +387,7 @@ final Pointer Function(
/// JSValue *jsNewObject(JSContext *ctx)
final Pointer Function(
Pointer ctx,
-) jsNewObject = qjsLib
+) jsNewObject = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -394,7 +400,7 @@ final void Function(
Pointer ctx,
Pointer val,
int free,
-) _jsFreeValue = qjsLib
+) _jsFreeValue = _qjsLib
.lookup<
NativeFunction<
Void Function(
@@ -417,7 +423,7 @@ final void Function(
Pointer rt,
Pointer val,
int free,
-) _jsFreeValueRT = qjsLib
+) _jsFreeValueRT = _qjsLib
.lookup<
NativeFunction<
Void Function(
@@ -439,7 +445,7 @@ void jsFreeValueRT(
final Pointer Function(
Pointer ctx,
Pointer val,
-) jsDupValue = qjsLib
+) jsDupValue = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -452,7 +458,7 @@ final Pointer Function(
final Pointer Function(
Pointer rt,
Pointer val,
-) jsDupValueRT = qjsLib
+) jsDupValueRT = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -465,7 +471,7 @@ final Pointer Function(
final int Function(
Pointer ctx,
Pointer val,
-) jsToBool = qjsLib
+) jsToBool = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -478,7 +484,7 @@ final int Function(
final int Function(
Pointer ctx,
Pointer val,
-) jsToInt64 = qjsLib
+) jsToInt64 = _qjsLib
.lookup<
NativeFunction<
Int64 Function(
@@ -491,7 +497,7 @@ final int Function(
final double Function(
Pointer ctx,
Pointer val,
-) jsToFloat64 = qjsLib
+) jsToFloat64 = _qjsLib
.lookup<
NativeFunction<
Double Function(
@@ -504,7 +510,7 @@ final double Function(
final Pointer Function(
Pointer ctx,
Pointer val,
-) _jsToCString = qjsLib
+) _jsToCString = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -517,7 +523,7 @@ final Pointer Function(
final void Function(
Pointer ctx,
Pointer val,
-) jsFreeCString = qjsLib
+) jsFreeCString = _qjsLib
.lookup<
NativeFunction<
Void Function(
@@ -541,7 +547,7 @@ String jsToCString(
final int Function(
Pointer ctx,
Pointer name,
-) _jsNewClass = qjsLib
+) _jsNewClass = _qjsLib
.lookup<
NativeFunction<
Uint32 Function(
@@ -568,7 +574,7 @@ final Pointer Function(
Pointer ctx,
int classId,
int opaque,
-) jsNewObjectClass = qjsLib
+) jsNewObjectClass = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -582,7 +588,7 @@ final Pointer Function(
final int Function(
Pointer obj,
int classid,
-) jsGetObjectOpaque = qjsLib
+) jsGetObjectOpaque = _qjsLib
.lookup<
NativeFunction<
IntPtr Function(
@@ -596,7 +602,7 @@ final Pointer Function(
Pointer ctx,
Pointer psize,
Pointer val,
-) jsGetArrayBuffer = qjsLib
+) jsGetArrayBuffer = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -610,7 +616,7 @@ final Pointer Function(
final int Function(
Pointer ctx,
Pointer val,
-) jsIsFunction = qjsLib
+) jsIsFunction = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -623,7 +629,7 @@ final int Function(
final int Function(
Pointer ctx,
Pointer val,
-) jsIsPromise = qjsLib
+) jsIsPromise = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -636,7 +642,7 @@ final int Function(
final int Function(
Pointer ctx,
Pointer val,
-) jsIsArray = qjsLib
+) jsIsArray = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -651,7 +657,7 @@ final Pointer Function(
Pointer ctx,
Pointer thisObj,
int prop,
-) jsGetProperty = qjsLib
+) jsGetProperty = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -669,7 +675,7 @@ final int Function(
int prop,
Pointer val,
int flag,
-) jsDefinePropertyValue = qjsLib
+) jsDefinePropertyValue = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -685,7 +691,7 @@ final int Function(
final Pointer Function(
Pointer ctx,
int v,
-) jsFreeAtom = qjsLib
+) jsFreeAtom = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -698,7 +704,7 @@ final Pointer Function(
final int Function(
Pointer ctx,
Pointer val,
-) jsValueToAtom = qjsLib
+) jsValueToAtom = _qjsLib
.lookup<
NativeFunction<
Uint32 Function(
@@ -711,7 +717,7 @@ final int Function(
final Pointer Function(
Pointer ctx,
int val,
-) jsAtomToValue = qjsLib
+) jsAtomToValue = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -728,7 +734,7 @@ final int Function(
Pointer plen,
Pointer obj,
int flags,
-) jsGetOwnPropertyNames = qjsLib
+) jsGetOwnPropertyNames = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -744,7 +750,7 @@ final int Function(
final int Function(
Pointer ptab,
int i,
-) jsPropertyEnumGetAtom = qjsLib
+) jsPropertyEnumGetAtom = _qjsLib
.lookup<
NativeFunction<
Uint32 Function(
@@ -754,7 +760,7 @@ final int Function(
.asFunction();
/// uint32_t sizeOfJSValue()
-final int Function() _sizeOfJSValue = qjsLib
+final int Function() _sizeOfJSValue = _qjsLib
.lookup>("sizeOfJSValue")
.asFunction();
@@ -765,7 +771,7 @@ final void Function(
Pointer list,
int i,
Pointer val,
-) setJSValueList = qjsLib
+) setJSValueList = _qjsLib
.lookup<
NativeFunction<
Void Function(
@@ -783,7 +789,7 @@ final Pointer Function(
Pointer thisObj,
int argc,
Pointer argv,
-) _jsCall = qjsLib
+) _jsCall = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -823,7 +829,7 @@ Pointer jsCall(
/// int jsIsException(JSValueConst *val)
final int Function(
Pointer val,
-) jsIsException = qjsLib
+) jsIsException = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -834,7 +840,7 @@ final int Function(
/// JSValue *jsGetException(JSContext *ctx)
final Pointer Function(
Pointer ctx,
-) jsGetException = qjsLib
+) jsGetException = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -845,7 +851,7 @@ final Pointer Function(
/// int jsExecutePendingJob(JSRuntime *rt)
final int Function(
Pointer ctx,
-) jsExecutePendingJob = qjsLib
+) jsExecutePendingJob = _qjsLib
.lookup<
NativeFunction<
Int32 Function(
@@ -857,7 +863,7 @@ final int Function(
final Pointer Function(
Pointer ctx,
Pointer resolvingFuncs,
-) jsNewPromiseCapability = qjsLib
+) jsNewPromiseCapability = _qjsLib
.lookup<
NativeFunction<
Pointer Function(
@@ -870,7 +876,7 @@ final Pointer Function(
final void Function(
Pointer ctx,
Pointer ptab,
-) jsFree = qjsLib
+) jsFree = _qjsLib
.lookup<
NativeFunction<
Void Function(
diff --git a/lib/flutter_qjs.dart b/lib/flutter_qjs.dart
index fee656a..e02deb8 100644
--- a/lib/flutter_qjs.dart
+++ b/lib/flutter_qjs.dart
@@ -10,10 +10,8 @@ import 'dart:ffi';
import 'dart:isolate';
import 'package:ffi/ffi.dart';
-import 'package:flutter_qjs/ffi.dart';
-import 'package:flutter_qjs/wrapper.dart';
-
-import 'isolate.dart';
+import 'ffi.dart';
+import 'wrapper.dart';
/// Handler function to manage js module.
typedef JsModuleHandler = String Function(String name);
@@ -21,6 +19,7 @@ typedef JsModuleHandler = String Function(String name);
/// Handler to manage unhandled promise rejection.
typedef JsHostPromiseRejectionHandler = void Function(String reason);
+/// Quickjs engine for flutter.
class FlutterQjs {
Pointer _rt;
Pointer _ctx;
@@ -37,21 +36,12 @@ class FlutterQjs {
/// Handler function to manage js module.
JsHostPromiseRejectionHandler hostPromiseRejectionHandler;
- /// Quickjs engine for flutter.
- ///
- /// Pass handlers to implement js-dart interaction and resolving modules.
FlutterQjs({
this.moduleHandler,
this.stackSize,
this.hostPromiseRejectionHandler,
});
- static applyFunction(Function func, List args, dynamic thisVal) {
- final passThis =
- RegExp("{.*thisVal.*}").hasMatch(func.runtimeType.toString());
- return Function.apply(func, args, passThis ? {#thisVal: thisVal} : null);
- }
-
_ensureEngine() {
if (_rt != null) return;
_rt = jsNewRuntime((ctx, type, ptr) {
@@ -69,10 +59,8 @@ class FlutterQjs {
)));
}
final thisVal = jsToDart(ctx, pdata.elementAt(0).value);
- final func = jsToDart(ctx, pdata.elementAt(3).value);
- final ret = func is QjsInvokable
- ? func.invoke(args, thisVal)
- : applyFunction(func, args, thisVal);
+ JSInvokable func = jsToDart(ctx, pdata.elementAt(3).value);
+ final ret = func.invoke(args, thisVal);
return dartToJs(ctx, ret);
case JSChannelType.MODULE:
if (moduleHandler == null) throw Exception("No ModuleHandler");
@@ -122,7 +110,7 @@ class FlutterQjs {
}, port);
if (this.stackSize != null && this.stackSize > 0)
jsSetMaxStackSize(_rt, this.stackSize);
- _ctx = jsNewContextWithPromsieWrapper(_rt);
+ _ctx = jsNewContext(_rt);
}
/// Free Runtime and Context which can be recreate when evaluate again.
@@ -146,18 +134,6 @@ class FlutterQjs {
break;
}
}
- List jsPromises = runtimeOpaques[_rt]
- .ref
- .where(
- (v) => v is JSPromise,
- )
- .toList();
- for (JSPromise jsPromise in jsPromises) {
- if (jsPromise.checkResolveReject()) {
- jsPromise.release();
- runtimeOpaques[_rt].ref.remove(jsPromise);
- }
- }
}
}
diff --git a/lib/isolate.dart b/lib/isolate.dart
index 47eea09..0180216 100644
--- a/lib/isolate.dart
+++ b/lib/isolate.dart
@@ -11,205 +11,8 @@ import 'dart:io';
import 'dart:isolate';
import 'package:ffi/ffi.dart';
-import 'package:flutter_qjs/flutter_qjs.dart';
-import 'package:flutter_qjs/wrapper.dart';
-
-class IsolateJSFunction implements QjsInvokable {
- int val;
- int ctx;
- SendPort port;
- IsolateJSFunction(this.ctx, this.val, this.port);
-
- Future invoke(List arguments, [thisVal]) async {
- if (0 == val ?? 0) return;
- var evaluatePort = ReceivePort();
- port.send({
- 'type': 'call',
- 'ctx': ctx,
- 'val': val,
- 'args': _encodeData(arguments),
- 'this': _encodeData(thisVal),
- 'port': evaluatePort.sendPort,
- });
- var result = await evaluatePort.first;
- evaluatePort.close();
- if (result['data'] != null)
- return _decodeData(result['data'], port);
- else
- throw result['error'];
- }
-
- @override
- noSuchMethod(Invocation invocation) {
- return invoke(
- invocation.positionalArguments,
- invocation.namedArguments[#thisVal],
- );
- }
-}
-
-class IsolateFunction implements QjsInvokable {
- SendPort _port;
- SendPort func;
- IsolateFunction(this.func, this._port);
-
- static IsolateFunction bind(Function func, SendPort port) {
- final funcPort = ReceivePort();
- funcPort.listen((msg) async {
- var data;
- SendPort msgPort = msg['port'];
- try {
- List args = _decodeData(msg['args'], port);
- Map thisVal = _decodeData(msg['this'], port);
- data = await FlutterQjs.applyFunction(func, args, thisVal);
- if (msgPort != null)
- msgPort.send({
- 'data': _encodeData(data),
- });
- } catch (e, stack) {
- if (msgPort != null)
- msgPort.send({
- 'error': e.toString() + "\n" + stack.toString(),
- });
- }
- });
- return IsolateFunction(funcPort.sendPort, port);
- }
-
- Future invoke(List positionalArguments, [thisVal]) async {
- if (func == null) return;
- var evaluatePort = ReceivePort();
- func.send({
- 'args': _encodeData(positionalArguments),
- 'this': _encodeData(thisVal),
- 'port': evaluatePort.sendPort,
- });
- var result = await evaluatePort.first;
- evaluatePort.close();
- if (result['data'] != null)
- return _decodeData(result['data'], _port);
- else
- throw result['error'];
- }
-
- @override
- noSuchMethod(Invocation invocation) {
- return invoke(
- invocation.positionalArguments,
- invocation.namedArguments[#thisVal],
- );
- }
-}
-
-dynamic _encodeData(dynamic data, {Map cache}) {
- if (cache == null) cache = Map();
- if (cache.containsKey(data)) return cache[data];
- if (data is List) {
- var ret = [];
- cache[data] = ret;
- for (int i = 0; i < data.length; ++i) {
- ret.add(_encodeData(data[i], cache: cache));
- }
- return ret;
- }
- if (data is Map) {
- var ret = {};
- cache[data] = ret;
- for (var entry in data.entries) {
- ret[_encodeData(entry.key, cache: cache)] =
- _encodeData(entry.value, cache: cache);
- }
- return ret;
- }
- if (data is JSFunction) {
- return {
- '__js_function_ctx': data.ctx.address,
- '__js_function_val': data.val.address,
- };
- }
- if (data is IsolateJSFunction) {
- return {
- '__js_function_ctx': data.ctx,
- '__js_function_val': data.val,
- };
- }
- if (data is IsolateFunction) {
- return {
- '__js_function_port': data.func,
- };
- }
- if (data is Future) {
- var futurePort = ReceivePort();
- data.then((value) {
- futurePort.first.then((port) {
- futurePort.close();
- (port as SendPort).send({'data': _encodeData(value)});
- });
- }, onError: (e, stack) {
- futurePort.first.then((port) {
- futurePort.close();
- (port as SendPort)
- .send({'error': e.toString() + "\n" + stack.toString()});
- });
- });
- return {
- '__js_future_port': futurePort.sendPort,
- };
- }
- return data;
-}
-
-dynamic _decodeData(dynamic data, SendPort port,
- {Map cache}) {
- if (cache == null) cache = Map();
- if (cache.containsKey(data)) return cache[data];
- if (data is List) {
- var ret = [];
- cache[data] = ret;
- for (int i = 0; i < data.length; ++i) {
- ret.add(_decodeData(data[i], port, cache: cache));
- }
- return ret;
- }
- if (data is Map) {
- if (data.containsKey('__js_function_val')) {
- int ctx = data['__js_function_ctx'];
- int val = data['__js_function_val'];
- if (port != null) {
- return IsolateJSFunction(ctx, val, port);
- } else {
- return JSFunction.fromAddress(ctx, val);
- }
- }
- if (data.containsKey('__js_function_port')) {
- return IsolateFunction(data['__js_function_port'], port);
- }
- if (data.containsKey('__js_future_port')) {
- SendPort port = data['__js_future_port'];
- var futurePort = ReceivePort();
- port.send(futurePort.sendPort);
- var futureCompleter = Completer();
- futureCompleter.future.catchError((e) {});
- futurePort.first.then((value) {
- futurePort.close();
- 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) {
- ret[_decodeData(entry.key, port, cache: cache)] =
- _decodeData(entry.value, port, cache: cache);
- }
- return ret;
- }
- return data;
-}
+import 'flutter_qjs.dart';
+import 'wrapper.dart';
void _runJsIsolate(Map spawnMessage) async {
SendPort sendPort = spawnMessage['port'];
@@ -256,11 +59,11 @@ void _runJsIsolate(Map spawnMessage) async {
break;
case 'call':
data = JSFunction.fromAddress(
- msg['ctx'],
- msg['val'],
+ Pointer.fromAddress(msg['ctx']),
+ Pointer.fromAddress(msg['val']),
).invoke(
- _decodeData(msg['args'], null),
- _decodeData(msg['this'], null),
+ decodeData(msg['args'], null),
+ decodeData(msg['this'], null),
);
break;
case 'close':
@@ -271,7 +74,7 @@ void _runJsIsolate(Map spawnMessage) async {
}
if (msgPort != null)
msgPort.send({
- 'data': _encodeData(data),
+ 'data': encodeData(data),
});
} catch (e, stack) {
if (msgPort != null)
@@ -389,10 +192,10 @@ class IsolateQjs {
'flag': evalFlags,
'port': evaluatePort.sendPort,
});
- var result = await evaluatePort.first;
+ Map result = await evaluatePort.first;
evaluatePort.close();
- if (result['error'] == null) {
- return _decodeData(result['data'], sendPort);
+ if (result.containsKey('data')) {
+ return decodeData(result['data'], sendPort);
} else
throw result['error'];
}
diff --git a/lib/wrapper.dart b/lib/wrapper.dart
index 600d327..51563cb 100644
--- a/lib/wrapper.dart
+++ b/lib/wrapper.dart
@@ -7,123 +7,20 @@
*/
import 'dart:async';
import 'dart:ffi';
+import 'dart:isolate';
import 'dart:typed_data';
-
import 'package:ffi/ffi.dart';
-
import 'ffi.dart';
-import 'isolate.dart';
-class JSRefValue implements JSRef {
- Pointer val;
- Pointer ctx;
- JSRefValue(this.ctx, Pointer val) {
- Pointer rt = jsGetRuntime(ctx);
- this.val = jsDupValue(ctx, val);
- runtimeOpaques[rt]?.ref?.add(this);
- }
+abstract class JSInvokable {
+ dynamic invoke(List args, [dynamic thisVal]);
- JSRefValue.fromAddress(int ctx, int val) {
- this.ctx = Pointer.fromAddress(ctx);
- this.val = Pointer.fromAddress(val);
- }
-
- @override
- void release() {
- if (val != null) {
- jsFreeValue(ctx, val);
- }
- val = null;
- ctx = null;
- }
-}
-
-abstract class QjsReleasable {
- void release();
-}
-
-abstract class QjsInvokable {
- dynamic invoke(List positionalArguments, [dynamic thisVal]);
-}
-
-class DartObject implements JSRef {
- Object obj;
- Pointer ctx;
- DartObject(this.ctx, this.obj) {
- runtimeOpaques[jsGetRuntime(ctx)]?.ref?.add(this);
- }
-
- static DartObject fromAddress(Pointer rt, int val) {
- return runtimeOpaques[rt]?.ref?.firstWhere(
- (e) => identityHashCode(e) == val,
- orElse: () => null,
- );
- }
-
- @override
- void release() {
- if (obj is QjsReleasable) (obj as QjsReleasable).release();
- obj = null;
- ctx = null;
- }
-}
-
-class JSPromise extends JSRefValue {
- Completer completer;
- JSPromise(Pointer ctx, Pointer val, this.completer) : super(ctx, val);
-
- @override
- void release() {
- super.release();
- if (!completer.isCompleted) {
- completer.completeError("Promise cannot resolve");
- }
- }
-
- bool checkResolveReject() {
- if (val == null || completer.isCompleted) return true;
- var status = jsToDart(ctx, val);
- if (status["__resolved"] == true) {
- completer.complete(status["__value"]);
- return true;
- }
- if (status["__rejected"] == true) {
- final err = jsGetPropertyStr(ctx, val, "__error");
- completer.completeError(parseJSException(
- ctx,
- perr: err,
- ));
- jsFreeValue(ctx, err);
- return true;
- }
- return false;
- }
-}
-
-class JSFunction extends JSRefValue implements QjsInvokable {
- JSFunction(Pointer ctx, Pointer val) : super(ctx, val);
-
- JSFunction.fromAddress(int ctx, int val) : super.fromAddress(ctx, val);
-
- invoke(List arguments, [dynamic thisVal]) {
- if (val == null) return;
- List args = arguments
- .map(
- (e) => dartToJs(ctx, e),
- )
- .toList();
- Pointer jsRet = jsCall(ctx, val, dartToJs(ctx, thisVal), args);
- for (Pointer jsArg in args) {
- jsFreeValue(ctx, jsArg);
- }
- bool isException = jsIsException(jsRet) != 0;
- if (isException) {
- jsFreeValue(ctx, jsRet);
- throw Exception(parseJSException(ctx));
- }
- var ret = jsToDart(ctx, jsRet);
- jsFreeValue(ctx, jsRet);
- return ret;
+ static dynamic wrap(dynamic func) {
+ return func is JSInvokable
+ ? func
+ : func is Function
+ ? _DartFunction(func)
+ : func;
}
@override
@@ -135,13 +32,316 @@ class JSFunction extends JSRefValue implements QjsInvokable {
}
}
-Pointer jsGetPropertyStr(Pointer ctx, Pointer val, String prop) {
- var jsAtomVal = jsNewString(ctx, prop);
- var jsAtom = jsValueToAtom(ctx, jsAtomVal);
- Pointer jsProp = jsGetProperty(ctx, val, jsAtom);
- jsFreeAtom(ctx, jsAtom);
- jsFreeValue(ctx, jsAtomVal);
- return jsProp;
+class _DartFunction extends JSInvokable {
+ Function _func;
+ _DartFunction(this._func);
+
+ @override
+ invoke(List args, [thisVal]) {
+ /// wrap this into function
+ final passThis =
+ RegExp("{.*thisVal.*}").hasMatch(_func.runtimeType.toString());
+ return Function.apply(_func, args, passThis ? {#thisVal: thisVal} : null);
+ }
+}
+
+abstract class DartReleasable {
+ void release();
+}
+
+class DartObject implements JSRef {
+ Object _obj;
+ Pointer _ctx;
+ DartObject(this._ctx, this._obj) {
+ runtimeOpaques[jsGetRuntime(_ctx)]?.ref?.add(this);
+ }
+
+ static DartObject fromAddress(Pointer rt, int val) {
+ return runtimeOpaques[rt]?.ref?.firstWhere(
+ (e) => identityHashCode(e) == val,
+ orElse: () => null,
+ );
+ }
+
+ @override
+ void release() {
+ if (_obj is DartReleasable) {
+ (_obj as DartReleasable).release();
+ }
+ _obj = null;
+ _ctx = null;
+ }
+}
+
+class JSObject implements JSRef {
+ Pointer _val;
+ Pointer _ctx;
+
+ /// Create
+ JSObject(this._ctx, Pointer _val) {
+ Pointer rt = jsGetRuntime(_ctx);
+ this._val = jsDupValue(_ctx, _val);
+ runtimeOpaques[rt]?.ref?.add(this);
+ }
+
+ JSObject.fromAddress(Pointer ctx, Pointer val) {
+ this._ctx = ctx;
+ this._val = val;
+ }
+
+ @override
+ void release() {
+ if (_val != null) {
+ jsFreeValue(_ctx, _val);
+ }
+ _val = null;
+ _ctx = null;
+ }
+}
+
+class JSFunction extends JSObject implements JSInvokable {
+ JSFunction(Pointer ctx, Pointer val) : super(ctx, val);
+
+ JSFunction.fromAddress(Pointer ctx, Pointer val)
+ : super.fromAddress(ctx, val);
+
+ @override
+ invoke(List arguments, [dynamic thisVal]) {
+ Pointer jsRet = _invoke(arguments, thisVal);
+ if (jsRet == null) return;
+ bool isException = jsIsException(jsRet) != 0;
+ if (isException) {
+ jsFreeValue(_ctx, jsRet);
+ throw Exception(parseJSException(_ctx));
+ }
+ var ret = jsToDart(_ctx, jsRet);
+ jsFreeValue(_ctx, jsRet);
+ return ret;
+ }
+
+ Pointer _invoke(List arguments, [dynamic thisVal]) {
+ if (_val == null) return null;
+ List args = arguments
+ .map(
+ (e) => dartToJs(_ctx, e),
+ )
+ .toList();
+ Pointer jsThis = dartToJs(_ctx, thisVal);
+ Pointer jsRet = jsCall(_ctx, _val, jsThis, args);
+ jsFreeValue(_ctx, jsThis);
+ for (Pointer jsArg in args) {
+ jsFreeValue(_ctx, jsArg);
+ }
+ return jsRet;
+ }
+
+ @override
+ noSuchMethod(Invocation invocation) {
+ return invoke(
+ invocation.positionalArguments,
+ invocation.namedArguments[#thisVal],
+ );
+ }
+}
+
+class IsolateJSFunction extends JSInvokable {
+ int _val;
+ int _ctx;
+ SendPort port;
+ IsolateJSFunction(this._ctx, this._val, this.port);
+
+ @override
+ Future invoke(List arguments, [thisVal]) async {
+ if (0 == _val ?? 0) return;
+ var evaluatePort = ReceivePort();
+ port.send({
+ 'type': 'call',
+ 'ctx': _ctx,
+ 'val': _val,
+ 'args': encodeData(arguments),
+ 'this': encodeData(thisVal),
+ 'port': evaluatePort.sendPort,
+ });
+ Map result = await evaluatePort.first;
+ evaluatePort.close();
+ if (result.containsKey('data'))
+ return decodeData(result['data'], port);
+ else
+ throw result['error'];
+ }
+}
+
+class IsolateFunction extends JSInvokable implements DartReleasable {
+ SendPort _port;
+ SendPort func;
+ IsolateFunction(this.func, this._port);
+
+ static IsolateFunction bind(Function func, SendPort port) {
+ final JSInvokable invokable = JSInvokable.wrap(func);
+ final funcPort = ReceivePort();
+ funcPort.listen((msg) async {
+ if (msg == "close") return funcPort.close();
+ var data;
+ SendPort msgPort = msg['port'];
+ try {
+ List args = decodeData(msg['args'], port);
+ Map thisVal = decodeData(msg['this'], port);
+ data = await invokable.invoke(args, thisVal);
+ if (msgPort != null)
+ msgPort.send({
+ 'data': encodeData(data),
+ });
+ } catch (e, stack) {
+ if (msgPort != null)
+ msgPort.send({
+ 'error': e.toString() + "\n" + stack.toString(),
+ });
+ }
+ });
+ return IsolateFunction(funcPort.sendPort, port);
+ }
+
+ @override
+ Future invoke(List positionalArguments, [thisVal]) async {
+ if (func == null) return;
+ var evaluatePort = ReceivePort();
+ func.send({
+ 'args': encodeData(positionalArguments),
+ 'this': encodeData(thisVal),
+ 'port': evaluatePort.sendPort,
+ });
+ Map result = await evaluatePort.first;
+ evaluatePort.close();
+ if (result.containsKey('data'))
+ return decodeData(result['data'], _port);
+ else
+ throw result['error'];
+ }
+
+ @override
+ void release() {
+ if (func == null) return;
+ func.send("close");
+ func = null;
+ }
+}
+
+dynamic encodeData(dynamic data, {Map cache}) {
+ if (cache == null) cache = Map();
+ if (cache.containsKey(data)) return cache[data];
+ if (data is List) {
+ var ret = [];
+ cache[data] = ret;
+ for (int i = 0; i < data.length; ++i) {
+ ret.add(encodeData(data[i], cache: cache));
+ }
+ return ret;
+ }
+ if (data is Map) {
+ var ret = {};
+ cache[data] = ret;
+ for (var entry in data.entries) {
+ ret[encodeData(entry.key, cache: cache)] =
+ encodeData(entry.value, cache: cache);
+ }
+ return ret;
+ }
+ if (data is JSObject) {
+ return {
+ '__js_function': data is JSFunction,
+ '__js_obj_ctx': data._ctx.address,
+ '__js_obj_val': data._val.address,
+ };
+ }
+ if (data is IsolateJSFunction) {
+ return {
+ '__js_obj_ctx': data._ctx,
+ '__js_obj_val': data._val,
+ };
+ }
+ if (data is IsolateFunction) {
+ return {
+ '__js_function_port': data.func,
+ };
+ }
+ if (data is Future) {
+ var futurePort = ReceivePort();
+ data.then((value) {
+ futurePort.first.then((port) {
+ futurePort.close();
+ (port as SendPort).send({'data': encodeData(value)});
+ });
+ }, onError: (e, stack) {
+ futurePort.first.then((port) {
+ futurePort.close();
+ (port as SendPort)
+ .send({'error': e.toString() + "\n" + stack.toString()});
+ });
+ });
+ return {
+ '__js_future_port': futurePort.sendPort,
+ };
+ }
+ return data;
+}
+
+dynamic decodeData(dynamic data, SendPort port, {Map cache}) {
+ if (cache == null) cache = Map();
+ if (cache.containsKey(data)) return cache[data];
+ if (data is List) {
+ var ret = [];
+ cache[data] = ret;
+ for (int i = 0; i < data.length; ++i) {
+ ret.add(decodeData(data[i], port, cache: cache));
+ }
+ return ret;
+ }
+ if (data is Map) {
+ if (data.containsKey('__js_obj_val')) {
+ int ctx = data['__js_obj_ctx'];
+ int val = data['__js_obj_val'];
+ if (data['__js_function'] == false) {
+ return JSObject.fromAddress(
+ Pointer.fromAddress(ctx),
+ Pointer.fromAddress(val),
+ );
+ } else if (port != null) {
+ return IsolateJSFunction(ctx, val, port);
+ } else {
+ return JSFunction.fromAddress(
+ Pointer.fromAddress(ctx),
+ Pointer.fromAddress(val),
+ );
+ }
+ }
+ if (data.containsKey('__js_function_port')) {
+ return IsolateFunction(data['__js_function_port'], port);
+ }
+ if (data.containsKey('__js_future_port')) {
+ SendPort port = data['__js_future_port'];
+ var futurePort = ReceivePort();
+ port.send(futurePort.sendPort);
+ var futureCompleter = Completer();
+ futureCompleter.future.catchError((e) {});
+ futurePort.first.then((value) {
+ futurePort.close();
+ 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) {
+ ret[decodeData(entry.key, port, cache: cache)] =
+ decodeData(entry.value, port, cache: cache);
+ }
+ return ret;
+ }
+ return data;
}
String parseJSException(Pointer ctx, {Pointer perr}) {
@@ -149,7 +349,7 @@ String parseJSException(Pointer ctx, {Pointer perr}) {
var err = jsToCString(ctx, e);
if (jsValueGetTag(e) == JSTag.OBJECT) {
- Pointer stack = jsGetPropertyStr(ctx, e, "stack");
+ Pointer stack = jsGetPropertyValue(ctx, e, "stack");
if (jsToBool(ctx, stack) != 0) {
err += '\n' + jsToCString(ctx, stack);
}
@@ -179,8 +379,23 @@ void definePropertyValue(
jsFreeValue(ctx, jsAtomVal);
}
+Pointer jsGetPropertyValue(
+ Pointer ctx,
+ Pointer obj,
+ dynamic key, {
+ Map cache,
+}) {
+ var jsAtomVal = dartToJs(ctx, key, cache: cache);
+ var jsAtom = jsValueToAtom(ctx, jsAtomVal);
+ var jsProp = jsGetProperty(ctx, obj, jsAtom);
+ jsFreeAtom(ctx, jsAtom);
+ jsFreeValue(ctx, jsAtomVal);
+ return jsProp;
+}
+
Pointer dartToJs(Pointer ctx, dynamic val, {Map cache}) {
if (val == null) return jsUNDEFINED();
+ if (val is JSObject) return jsDupValue(ctx, val._val);
if (val is Future) {
var resolvingFunc = allocate(count: sizeOfJSValue * 2);
var resolvingFunc2 =
@@ -214,9 +429,6 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map cache}) {
if (cache.containsKey(val)) {
return jsDupValue(ctx, cache[val]);
}
- if (val is JSFunction) {
- return jsDupValue(ctx, val.val);
- }
if (val is List) {
Pointer ret = jsNewArray(ctx);
cache[val] = ret;
@@ -233,15 +445,17 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map cache}) {
}
return ret;
}
+ // wrap Function to JSInvokable
+ final valWrap = JSInvokable.wrap(val);
int dartObjectClassId =
runtimeOpaques[jsGetRuntime(ctx)]?.dartObjectClassId ?? 0;
if (dartObjectClassId == 0) return jsUNDEFINED();
var dartObject = jsNewObjectClass(
ctx,
dartObjectClassId,
- identityHashCode(DartObject(ctx, val)),
+ identityHashCode(DartObject(ctx, valWrap)),
);
- if (val is Function || val is IsolateFunction) {
+ if (valWrap is JSInvokable) {
final ret = jsNewCFunction(ctx, dartObject);
jsFreeValue(ctx, dartObject);
return ret;
@@ -268,7 +482,7 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map cache}) {
if (dartObjectClassId != 0) {
final dartObject = DartObject.fromAddress(
rt, jsGetObjectOpaque(val, dartObjectClassId));
- if (dartObject != null) return dartObject.obj;
+ if (dartObject != null) return dartObject._obj;
}
Pointer psize = allocate();
Pointer buf = jsGetArrayBuffer(ctx, psize, val);
@@ -284,18 +498,30 @@ 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[rt]?.promiseToFuture(val);
+ Pointer jsPromiseThen = jsGetPropertyValue(ctx, val, "then");
+ JSFunction promiseThen = jsToDart(ctx, jsPromiseThen, cache: cache);
+ jsFreeValue(ctx, jsPromiseThen);
+ var completer = Completer();
+ completer.future.catchError((e) {});
+ final jsRet = promiseThen._invoke([
+ (v) {
+ if (!completer.isCompleted) completer.complete(v);
+ },
+ (e) {
+ if (!completer.isCompleted) completer.completeError(e);
+ },
+ ], JSObject.fromAddress(ctx, val));
+ bool isException = jsIsException(jsRet) != 0;
+ jsFreeValue(ctx, jsRet);
+ if (isException) throw Exception(parseJSException(ctx));
+ return completer.future;
} else if (jsIsArray(ctx, val) != 0) {
- Pointer jslength = jsGetPropertyStr(ctx, val, "length");
+ Pointer jslength = jsGetPropertyValue(ctx, val, "length");
int length = jsToInt64(ctx, jslength);
List ret = [];
cache[valptr] = ret;
for (int i = 0; i < length; ++i) {
- var jsAtomVal = jsNewInt64(ctx, i);
- var jsAtom = jsValueToAtom(ctx, jsAtomVal);
- var jsProp = jsGetProperty(ctx, val, jsAtom);
- jsFreeAtom(ctx, jsAtom);
- jsFreeValue(ctx, jsAtomVal);
+ var jsProp = jsGetPropertyValue(ctx, val, i);
ret.add(jsToDart(ctx, jsProp, cache: cache));
jsFreeValue(ctx, jsProp);
}
@@ -327,43 +553,3 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map cache}) {
}
return null;
}
-
-Pointer jsNewContextWithPromsieWrapper(Pointer rt) {
- var ctx = jsNewContext(rt);
- final runtimeOpaque = runtimeOpaques[rt];
- if (runtimeOpaque == null) throw Exception("Runtime has been released!");
-
- var jsPromiseWrapper = jsEval(
- ctx,
- """
- (value) => {
- const __ret = {};
- Promise.resolve(value)
- .then(v => {
- __ret.__value = v;
- __ret.__resolved = true;
- }).catch(e => {
- __ret.__error = e;
- __ret.__rejected = true;
- });
- return __ret;
- }
- """,
- "",
- JSEvalFlag.GLOBAL);
- runtimeOpaque.dartObjectClassId = jsNewClass(ctx, "DartObject");
- final promiseWrapper = JSRefValue(ctx, jsPromiseWrapper);
- jsFreeValue(ctx, jsPromiseWrapper);
- runtimeOpaque.promiseToFuture = (promise) {
- var completer = Completer();
- completer.future.catchError((e) {});
- var wrapper = promiseWrapper.val;
- if (wrapper == null)
- completer.completeError(Exception("Runtime has been released!"));
- var jsPromise = jsCall(ctx, wrapper, null, [promise]);
- var wrapPromise = JSPromise(ctx, jsPromise, completer);
- jsFreeValue(ctx, jsPromise);
- return wrapPromise.completer.future;
- };
- return ctx;
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index 7f636c7..6c6deaa 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.0
+version: 0.3.1
homepage: https://github.com/ekibun/flutter_qjs
environment:
diff --git a/test/flutter_qjs_test.dart b/test/flutter_qjs_test.dart
index 9170172..923a6a3 100644
--- a/test/flutter_qjs_test.dart
+++ b/test/flutter_qjs_test.dart
@@ -9,32 +9,46 @@ 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_qjs/ffi.dart';
import 'package:flutter_test/flutter_test.dart';
-dynamic myFunction(String args, {String thisVal}) {
+dynamic myFunction(String args, {thisVal}) {
return [thisVal, args];
}
Future testEvaluate(qjs) async {
- final testWrap = await qjs.evaluate("(a) => a", name: "");
+ final testWrap = await qjs.evaluate(
+ "(a) => a",
+ name: "",
+ );
+ final wrapNull = await testWrap(null);
+ expect(wrapNull, null, reason: "wrap null");
final primities = [0, 1, 0.1, true, false, "str"];
final wrapPrimities = await testWrap(primities);
for (var i = 0; i < primities.length; i++) {
expect(wrapPrimities[i], primities[i], reason: "wrap primities");
}
+ final wrapFunction = await testWrap(testWrap);
+ final testEqual = await qjs.evaluate(
+ "(a, b) => a === b",
+ name: "",
+ );
+ expect(await testEqual(wrapFunction, testWrap), true,
+ reason: "wrap function");
+
+ expect(wrapNull, null, reason: "wrap null");
final a = {};
a["a"] = a;
final wrapA = await testWrap(a);
expect(wrapA['a'], wrapA, reason: "recursive object");
final testThis = await qjs.evaluate(
- "(func) => func.call('this', 'arg')",
+ "(function (func, arg) { return func.call(this, arg) })",
name: "",
);
- final funcRet = await testThis(myFunction);
- expect(funcRet[0], 'this', reason: "js function this");
+ final funcRet = await testThis(myFunction, 'arg', thisVal: {'name': 'this'});
+ expect(funcRet[0]['name'], 'this', reason: "js function this");
expect(funcRet[1], 'arg', reason: "js function argument");
final promises = await testWrap(await qjs.evaluate(
"[Promise.reject('test Promise.reject'), Promise.resolve('test Promise.resolve')]",