mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 05:27:23 +00:00
v0.3.1
This commit is contained in:
@@ -6,6 +6,11 @@
|
|||||||
* @LastEditTime: 2020-12-02 11:36:40
|
* @LastEditTime: 2020-12-02 11:36:40
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
|
* code clean up.
|
||||||
|
* fix isolate wrap error.
|
||||||
|
|
||||||
## 0.3.0
|
## 0.3.0
|
||||||
|
|
||||||
* breakdown change to remove `channel`.
|
* breakdown change to remove `channel`.
|
||||||
|
46
README.md
46
README.md
@@ -45,20 +45,31 @@ engine = null;
|
|||||||
|
|
||||||
Data conversion between dart and js are implemented as follow:
|
Data conversion between dart and js are implemented as follow:
|
||||||
|
|
||||||
| dart | js |
|
| dart | js |
|
||||||
| --------- | ------------------ |
|
| ----------------------- | ------------------ |
|
||||||
| Bool | boolean |
|
| Bool | boolean |
|
||||||
| Int | number |
|
| Int | number |
|
||||||
| Double | number |
|
| Double | number |
|
||||||
| String | string |
|
| String | string |
|
||||||
| Uint8List | ArrayBuffer |
|
| Uint8List | ArrayBuffer |
|
||||||
| List | Array |
|
| List | Array |
|
||||||
| Map | Object |
|
| Map | Object |
|
||||||
| Function | function(....args) |
|
| Function<br>JSInvokable | function(....args) |
|
||||||
| Future | Promise |
|
| Future | Promise |
|
||||||
| Object | DartObject |
|
| 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
|
### Use modules
|
||||||
|
|
||||||
@@ -119,10 +130,11 @@ try {
|
|||||||
|
|
||||||
Method `close` can destroy quickjs runtime that can be recreated again if you call `evaluate`.
|
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
|
```dart
|
||||||
await setToGlobalObject("func", await engine.bind(() {
|
await setToGlobalObject("func", await engine.bind(({thisVal}) {
|
||||||
// DO SOMETHING
|
// DO SOMETHING
|
||||||
}))
|
}))
|
||||||
```
|
```
|
||||||
@@ -131,8 +143,8 @@ await setToGlobalObject("func", await engine.bind(() {
|
|||||||
|
|
||||||
## Breaking change in v0.3.0
|
## Breaking change in v0.3.0
|
||||||
|
|
||||||
`channel` function is no longer utilized by default.
|
`channel` function is no longer included by default.
|
||||||
Use js function to set to global:
|
Use js function to set dart object globally:
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
final setToGlobalObject = await engine.evaluate("(key, val) => this[key] = val;");
|
final setToGlobalObject = await engine.evaluate("(key, val) => this[key] = val;");
|
||||||
|
@@ -1,16 +1,3 @@
|
|||||||
# flutter_qjs_example
|
# flutter_qjs_example
|
||||||
|
|
||||||
Demonstrates how to use the flutter_qjs plugin.
|
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.
|
|
||||||
|
@@ -6,9 +6,7 @@
|
|||||||
* @LastEditTime: 2020-12-02 11:28:06
|
* @LastEditTime: 2020-12-02 11:28:06
|
||||||
*/
|
*/
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_qjs/isolate.dart';
|
import 'package:flutter_qjs/isolate.dart';
|
||||||
|
|
||||||
@@ -44,28 +42,6 @@ class TestPage extends StatefulWidget {
|
|||||||
State<StatefulWidget> createState() => _TestPageState();
|
State<StatefulWidget> 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<TestPage> {
|
class _TestPageState extends State<TestPage> {
|
||||||
String resp;
|
String resp;
|
||||||
IsolateQjs engine;
|
IsolateQjs engine;
|
||||||
@@ -82,9 +58,6 @@ class _TestPageState extends State<TestPage> {
|
|||||||
"js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
|
"js/" + module.replaceFirst(new RegExp(r".js$"), "") + ".js");
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final setToGlobalObject =
|
|
||||||
await engine.evaluate("(key, val) => this[key] = val;");
|
|
||||||
setToGlobalObject("channel", methodHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@@ -43,13 +43,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0-nullsafety.5"
|
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:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -82,7 +75,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.3.0"
|
version: "0.3.1"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -95,13 +88,6 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
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:
|
matcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@@ -22,7 +22,6 @@ dependencies:
|
|||||||
|
|
||||||
highlight: 0.6.0
|
highlight: 0.6.0
|
||||||
flutter_highlight: 0.6.0
|
flutter_highlight: 0.6.0
|
||||||
dio: 3.0.10
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
116
lib/ffi.dart
116
lib/ffi.dart
@@ -15,7 +15,6 @@ abstract class JSRef {
|
|||||||
void release();
|
void release();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// JS_Eval() flags
|
|
||||||
class JSEvalFlag {
|
class JSEvalFlag {
|
||||||
static const GLOBAL = 0 << 0;
|
static const GLOBAL = 0 << 0;
|
||||||
static const MODULE = 1 << 0;
|
static const MODULE = 1 << 0;
|
||||||
@@ -56,7 +55,7 @@ class JSTag {
|
|||||||
static const FLOAT64 = 7;
|
static const FLOAT64 = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
final DynamicLibrary qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
|
final DynamicLibrary _qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
|
||||||
? (Platform.isWindows
|
? (Platform.isWindows
|
||||||
? DynamicLibrary.open("test/build/Debug/ffiquickjs.dll")
|
? DynamicLibrary.open("test/build/Debug/ffiquickjs.dll")
|
||||||
: Platform.isMacOS
|
: Platform.isMacOS
|
||||||
@@ -72,7 +71,7 @@ final DynamicLibrary qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer<Utf8> message,
|
Pointer<Utf8> message,
|
||||||
) _jsThrowInternalError = qjsLib
|
) _jsThrowInternalError = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -89,12 +88,12 @@ Pointer jsThrowInternalError(Pointer ctx, String message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// JSValue *jsEXCEPTION()
|
/// JSValue *jsEXCEPTION()
|
||||||
final Pointer Function() jsEXCEPTION = qjsLib
|
final Pointer Function() jsEXCEPTION = _qjsLib
|
||||||
.lookup<NativeFunction<Pointer Function()>>("jsEXCEPTION")
|
.lookup<NativeFunction<Pointer Function()>>("jsEXCEPTION")
|
||||||
.asFunction();
|
.asFunction();
|
||||||
|
|
||||||
/// JSValue *jsUNDEFINED()
|
/// JSValue *jsUNDEFINED()
|
||||||
final Pointer Function() jsUNDEFINED = qjsLib
|
final Pointer Function() jsUNDEFINED = _qjsLib
|
||||||
.lookup<NativeFunction<Pointer Function()>>("jsUNDEFINED")
|
.lookup<NativeFunction<Pointer Function()>>("jsUNDEFINED")
|
||||||
.asFunction();
|
.asFunction();
|
||||||
|
|
||||||
@@ -105,7 +104,7 @@ typedef JSChannelNative = Pointer Function(
|
|||||||
/// JSRuntime *jsNewRuntime(JSChannel channel)
|
/// JSRuntime *jsNewRuntime(JSChannel channel)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer<NativeFunction<JSChannelNative>>,
|
Pointer<NativeFunction<JSChannelNative>>,
|
||||||
) _jsNewRuntime = qjsLib
|
) _jsNewRuntime = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -117,7 +116,6 @@ class RuntimeOpaque {
|
|||||||
JSChannel channel;
|
JSChannel channel;
|
||||||
List<JSRef> ref = [];
|
List<JSRef> ref = [];
|
||||||
ReceivePort port;
|
ReceivePort port;
|
||||||
Future Function(Pointer) promiseToFuture;
|
|
||||||
int dartObjectClassId;
|
int dartObjectClassId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,7 +141,7 @@ Pointer jsNewRuntime(
|
|||||||
final void Function(
|
final void Function(
|
||||||
Pointer,
|
Pointer,
|
||||||
int,
|
int,
|
||||||
) jsSetMaxStackSize = qjsLib
|
) jsSetMaxStackSize = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
@@ -155,7 +153,7 @@ final void Function(
|
|||||||
/// void jsFreeRuntime(JSRuntime *rt)
|
/// void jsFreeRuntime(JSRuntime *rt)
|
||||||
final void Function(
|
final void Function(
|
||||||
Pointer,
|
Pointer,
|
||||||
) _jsFreeRuntime = qjsLib
|
) _jsFreeRuntime = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
@@ -177,7 +175,7 @@ void jsFreeRuntime(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer funcData,
|
Pointer funcData,
|
||||||
) jsNewCFunction = qjsLib
|
) jsNewCFunction = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -189,7 +187,7 @@ final Pointer Function(
|
|||||||
/// JSContext *jsNewContext(JSRuntime *rt)
|
/// JSContext *jsNewContext(JSRuntime *rt)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer rt,
|
Pointer rt,
|
||||||
) jsNewContext = qjsLib
|
) _jsNewContext = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -197,10 +195,18 @@ final Pointer Function(
|
|||||||
)>>("jsNewContext")
|
)>>("jsNewContext")
|
||||||
.asFunction();
|
.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)
|
/// void jsFreeContext(JSContext *ctx)
|
||||||
final void Function(
|
final void Function(
|
||||||
Pointer,
|
Pointer,
|
||||||
) jsFreeContext = qjsLib
|
) jsFreeContext = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
@@ -211,7 +217,7 @@ final void Function(
|
|||||||
/// JSRuntime *jsGetRuntime(JSContext *ctx)
|
/// JSRuntime *jsGetRuntime(JSContext *ctx)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer,
|
Pointer,
|
||||||
) jsGetRuntime = qjsLib
|
) jsGetRuntime = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -226,7 +232,7 @@ final Pointer Function(
|
|||||||
int inputLen,
|
int inputLen,
|
||||||
Pointer<Utf8> filename,
|
Pointer<Utf8> filename,
|
||||||
int evalFlags,
|
int evalFlags,
|
||||||
) _jsEval = qjsLib
|
) _jsEval = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -262,7 +268,7 @@ Pointer jsEval(
|
|||||||
/// DLLEXPORT int32_t jsValueGetTag(JSValue *val)
|
/// DLLEXPORT int32_t jsValueGetTag(JSValue *val)
|
||||||
final int Function(
|
final int Function(
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsValueGetTag = qjsLib
|
) jsValueGetTag = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -273,7 +279,7 @@ final int Function(
|
|||||||
/// void *jsValueGetPtr(JSValue *val)
|
/// void *jsValueGetPtr(JSValue *val)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsValueGetPtr = qjsLib
|
) jsValueGetPtr = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -284,7 +290,7 @@ final Pointer Function(
|
|||||||
/// DLLEXPORT bool jsTagIsFloat64(int32_t tag)
|
/// DLLEXPORT bool jsTagIsFloat64(int32_t tag)
|
||||||
final int Function(
|
final int Function(
|
||||||
int val,
|
int val,
|
||||||
) jsTagIsFloat64 = qjsLib
|
) jsTagIsFloat64 = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -296,7 +302,7 @@ final int Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
int val,
|
int val,
|
||||||
) jsNewBool = qjsLib
|
) jsNewBool = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -309,7 +315,7 @@ final Pointer Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
int val,
|
int val,
|
||||||
) jsNewInt64 = qjsLib
|
) jsNewInt64 = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -322,7 +328,7 @@ final Pointer Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
double val,
|
double val,
|
||||||
) jsNewFloat64 = qjsLib
|
) jsNewFloat64 = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -335,7 +341,7 @@ final Pointer Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer<Utf8> str,
|
Pointer<Utf8> str,
|
||||||
) _jsNewString = qjsLib
|
) _jsNewString = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -357,7 +363,7 @@ final Pointer Function(
|
|||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer<Uint8> buf,
|
Pointer<Uint8> buf,
|
||||||
int len,
|
int len,
|
||||||
) jsNewArrayBufferCopy = qjsLib
|
) jsNewArrayBufferCopy = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -370,7 +376,7 @@ final Pointer Function(
|
|||||||
/// JSValue *jsNewArray(JSContext *ctx)
|
/// JSValue *jsNewArray(JSContext *ctx)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
) jsNewArray = qjsLib
|
) jsNewArray = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -381,7 +387,7 @@ final Pointer Function(
|
|||||||
/// JSValue *jsNewObject(JSContext *ctx)
|
/// JSValue *jsNewObject(JSContext *ctx)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
) jsNewObject = qjsLib
|
) jsNewObject = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -394,7 +400,7 @@ final void Function(
|
|||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
int free,
|
int free,
|
||||||
) _jsFreeValue = qjsLib
|
) _jsFreeValue = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
@@ -417,7 +423,7 @@ final void Function(
|
|||||||
Pointer rt,
|
Pointer rt,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
int free,
|
int free,
|
||||||
) _jsFreeValueRT = qjsLib
|
) _jsFreeValueRT = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
@@ -439,7 +445,7 @@ void jsFreeValueRT(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsDupValue = qjsLib
|
) jsDupValue = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -452,7 +458,7 @@ final Pointer Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer rt,
|
Pointer rt,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsDupValueRT = qjsLib
|
) jsDupValueRT = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -465,7 +471,7 @@ final Pointer Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsToBool = qjsLib
|
) jsToBool = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -478,7 +484,7 @@ final int Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsToInt64 = qjsLib
|
) jsToInt64 = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int64 Function(
|
Int64 Function(
|
||||||
@@ -491,7 +497,7 @@ final int Function(
|
|||||||
final double Function(
|
final double Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsToFloat64 = qjsLib
|
) jsToFloat64 = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Double Function(
|
Double Function(
|
||||||
@@ -504,7 +510,7 @@ final double Function(
|
|||||||
final Pointer<Utf8> Function(
|
final Pointer<Utf8> Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) _jsToCString = qjsLib
|
) _jsToCString = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer<Utf8> Function(
|
Pointer<Utf8> Function(
|
||||||
@@ -517,7 +523,7 @@ final Pointer<Utf8> Function(
|
|||||||
final void Function(
|
final void Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer<Utf8> val,
|
Pointer<Utf8> val,
|
||||||
) jsFreeCString = qjsLib
|
) jsFreeCString = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
@@ -541,7 +547,7 @@ String jsToCString(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer<Utf8> name,
|
Pointer<Utf8> name,
|
||||||
) _jsNewClass = qjsLib
|
) _jsNewClass = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Uint32 Function(
|
Uint32 Function(
|
||||||
@@ -568,7 +574,7 @@ final Pointer Function(
|
|||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
int classId,
|
int classId,
|
||||||
int opaque,
|
int opaque,
|
||||||
) jsNewObjectClass = qjsLib
|
) jsNewObjectClass = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -582,7 +588,7 @@ final Pointer Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer obj,
|
Pointer obj,
|
||||||
int classid,
|
int classid,
|
||||||
) jsGetObjectOpaque = qjsLib
|
) jsGetObjectOpaque = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
IntPtr Function(
|
IntPtr Function(
|
||||||
@@ -596,7 +602,7 @@ final Pointer<Uint8> Function(
|
|||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer<IntPtr> psize,
|
Pointer<IntPtr> psize,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsGetArrayBuffer = qjsLib
|
) jsGetArrayBuffer = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer<Uint8> Function(
|
Pointer<Uint8> Function(
|
||||||
@@ -610,7 +616,7 @@ final Pointer<Uint8> Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsIsFunction = qjsLib
|
) jsIsFunction = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -623,7 +629,7 @@ final int Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsIsPromise = qjsLib
|
) jsIsPromise = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -636,7 +642,7 @@ final int Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsIsArray = qjsLib
|
) jsIsArray = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -651,7 +657,7 @@ final Pointer Function(
|
|||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer thisObj,
|
Pointer thisObj,
|
||||||
int prop,
|
int prop,
|
||||||
) jsGetProperty = qjsLib
|
) jsGetProperty = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -669,7 +675,7 @@ final int Function(
|
|||||||
int prop,
|
int prop,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
int flag,
|
int flag,
|
||||||
) jsDefinePropertyValue = qjsLib
|
) jsDefinePropertyValue = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -685,7 +691,7 @@ final int Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
int v,
|
int v,
|
||||||
) jsFreeAtom = qjsLib
|
) jsFreeAtom = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -698,7 +704,7 @@ final Pointer Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsValueToAtom = qjsLib
|
) jsValueToAtom = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Uint32 Function(
|
Uint32 Function(
|
||||||
@@ -711,7 +717,7 @@ final int Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
int val,
|
int val,
|
||||||
) jsAtomToValue = qjsLib
|
) jsAtomToValue = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -728,7 +734,7 @@ final int Function(
|
|||||||
Pointer<Uint32> plen,
|
Pointer<Uint32> plen,
|
||||||
Pointer obj,
|
Pointer obj,
|
||||||
int flags,
|
int flags,
|
||||||
) jsGetOwnPropertyNames = qjsLib
|
) jsGetOwnPropertyNames = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -744,7 +750,7 @@ final int Function(
|
|||||||
final int Function(
|
final int Function(
|
||||||
Pointer ptab,
|
Pointer ptab,
|
||||||
int i,
|
int i,
|
||||||
) jsPropertyEnumGetAtom = qjsLib
|
) jsPropertyEnumGetAtom = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Uint32 Function(
|
Uint32 Function(
|
||||||
@@ -754,7 +760,7 @@ final int Function(
|
|||||||
.asFunction();
|
.asFunction();
|
||||||
|
|
||||||
/// uint32_t sizeOfJSValue()
|
/// uint32_t sizeOfJSValue()
|
||||||
final int Function() _sizeOfJSValue = qjsLib
|
final int Function() _sizeOfJSValue = _qjsLib
|
||||||
.lookup<NativeFunction<Uint32 Function()>>("sizeOfJSValue")
|
.lookup<NativeFunction<Uint32 Function()>>("sizeOfJSValue")
|
||||||
.asFunction();
|
.asFunction();
|
||||||
|
|
||||||
@@ -765,7 +771,7 @@ final void Function(
|
|||||||
Pointer list,
|
Pointer list,
|
||||||
int i,
|
int i,
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) setJSValueList = qjsLib
|
) setJSValueList = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
@@ -783,7 +789,7 @@ final Pointer Function(
|
|||||||
Pointer thisObj,
|
Pointer thisObj,
|
||||||
int argc,
|
int argc,
|
||||||
Pointer argv,
|
Pointer argv,
|
||||||
) _jsCall = qjsLib
|
) _jsCall = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -823,7 +829,7 @@ Pointer jsCall(
|
|||||||
/// int jsIsException(JSValueConst *val)
|
/// int jsIsException(JSValueConst *val)
|
||||||
final int Function(
|
final int Function(
|
||||||
Pointer val,
|
Pointer val,
|
||||||
) jsIsException = qjsLib
|
) jsIsException = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -834,7 +840,7 @@ final int Function(
|
|||||||
/// JSValue *jsGetException(JSContext *ctx)
|
/// JSValue *jsGetException(JSContext *ctx)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
) jsGetException = qjsLib
|
) jsGetException = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -845,7 +851,7 @@ final Pointer Function(
|
|||||||
/// int jsExecutePendingJob(JSRuntime *rt)
|
/// int jsExecutePendingJob(JSRuntime *rt)
|
||||||
final int Function(
|
final int Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
) jsExecutePendingJob = qjsLib
|
) jsExecutePendingJob = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Int32 Function(
|
Int32 Function(
|
||||||
@@ -857,7 +863,7 @@ final int Function(
|
|||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer resolvingFuncs,
|
Pointer resolvingFuncs,
|
||||||
) jsNewPromiseCapability = qjsLib
|
) jsNewPromiseCapability = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
@@ -870,7 +876,7 @@ final Pointer Function(
|
|||||||
final void Function(
|
final void Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer ptab,
|
Pointer ptab,
|
||||||
) jsFree = qjsLib
|
) jsFree = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Void Function(
|
Void Function(
|
||||||
|
@@ -10,10 +10,8 @@ import 'dart:ffi';
|
|||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:flutter_qjs/ffi.dart';
|
import 'ffi.dart';
|
||||||
import 'package:flutter_qjs/wrapper.dart';
|
import 'wrapper.dart';
|
||||||
|
|
||||||
import 'isolate.dart';
|
|
||||||
|
|
||||||
/// Handler function to manage js module.
|
/// Handler function to manage js module.
|
||||||
typedef JsModuleHandler = String Function(String name);
|
typedef JsModuleHandler = String Function(String name);
|
||||||
@@ -21,6 +19,7 @@ typedef JsModuleHandler = String Function(String name);
|
|||||||
/// Handler to manage unhandled promise rejection.
|
/// Handler to manage unhandled promise rejection.
|
||||||
typedef JsHostPromiseRejectionHandler = void Function(String reason);
|
typedef JsHostPromiseRejectionHandler = void Function(String reason);
|
||||||
|
|
||||||
|
/// Quickjs engine for flutter.
|
||||||
class FlutterQjs {
|
class FlutterQjs {
|
||||||
Pointer _rt;
|
Pointer _rt;
|
||||||
Pointer _ctx;
|
Pointer _ctx;
|
||||||
@@ -37,21 +36,12 @@ class FlutterQjs {
|
|||||||
/// Handler function to manage js module.
|
/// Handler function to manage js module.
|
||||||
JsHostPromiseRejectionHandler hostPromiseRejectionHandler;
|
JsHostPromiseRejectionHandler hostPromiseRejectionHandler;
|
||||||
|
|
||||||
/// Quickjs engine for flutter.
|
|
||||||
///
|
|
||||||
/// Pass handlers to implement js-dart interaction and resolving modules.
|
|
||||||
FlutterQjs({
|
FlutterQjs({
|
||||||
this.moduleHandler,
|
this.moduleHandler,
|
||||||
this.stackSize,
|
this.stackSize,
|
||||||
this.hostPromiseRejectionHandler,
|
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() {
|
_ensureEngine() {
|
||||||
if (_rt != null) return;
|
if (_rt != null) return;
|
||||||
_rt = jsNewRuntime((ctx, type, ptr) {
|
_rt = jsNewRuntime((ctx, type, ptr) {
|
||||||
@@ -69,10 +59,8 @@ class FlutterQjs {
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
final thisVal = jsToDart(ctx, pdata.elementAt(0).value);
|
final thisVal = jsToDart(ctx, pdata.elementAt(0).value);
|
||||||
final func = jsToDart(ctx, pdata.elementAt(3).value);
|
JSInvokable func = jsToDart(ctx, pdata.elementAt(3).value);
|
||||||
final ret = func is QjsInvokable
|
final ret = func.invoke(args, thisVal);
|
||||||
? func.invoke(args, thisVal)
|
|
||||||
: applyFunction(func, args, thisVal);
|
|
||||||
return dartToJs(ctx, ret);
|
return dartToJs(ctx, ret);
|
||||||
case JSChannelType.MODULE:
|
case JSChannelType.MODULE:
|
||||||
if (moduleHandler == null) throw Exception("No ModuleHandler");
|
if (moduleHandler == null) throw Exception("No ModuleHandler");
|
||||||
@@ -122,7 +110,7 @@ class FlutterQjs {
|
|||||||
}, port);
|
}, port);
|
||||||
if (this.stackSize != null && this.stackSize > 0)
|
if (this.stackSize != null && this.stackSize > 0)
|
||||||
jsSetMaxStackSize(_rt, this.stackSize);
|
jsSetMaxStackSize(_rt, this.stackSize);
|
||||||
_ctx = jsNewContextWithPromsieWrapper(_rt);
|
_ctx = jsNewContext(_rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Free Runtime and Context which can be recreate when evaluate again.
|
/// Free Runtime and Context which can be recreate when evaluate again.
|
||||||
@@ -146,18 +134,6 @@ class FlutterQjs {
|
|||||||
break;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
217
lib/isolate.dart
217
lib/isolate.dart
@@ -11,205 +11,8 @@ import 'dart:io';
|
|||||||
import 'dart:isolate';
|
import 'dart:isolate';
|
||||||
|
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:flutter_qjs/flutter_qjs.dart';
|
import 'flutter_qjs.dart';
|
||||||
import 'package:flutter_qjs/wrapper.dart';
|
import '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<dynamic, dynamic> 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<dynamic, dynamic> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void _runJsIsolate(Map spawnMessage) async {
|
void _runJsIsolate(Map spawnMessage) async {
|
||||||
SendPort sendPort = spawnMessage['port'];
|
SendPort sendPort = spawnMessage['port'];
|
||||||
@@ -256,11 +59,11 @@ void _runJsIsolate(Map spawnMessage) async {
|
|||||||
break;
|
break;
|
||||||
case 'call':
|
case 'call':
|
||||||
data = JSFunction.fromAddress(
|
data = JSFunction.fromAddress(
|
||||||
msg['ctx'],
|
Pointer.fromAddress(msg['ctx']),
|
||||||
msg['val'],
|
Pointer.fromAddress(msg['val']),
|
||||||
).invoke(
|
).invoke(
|
||||||
_decodeData(msg['args'], null),
|
decodeData(msg['args'], null),
|
||||||
_decodeData(msg['this'], null),
|
decodeData(msg['this'], null),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case 'close':
|
case 'close':
|
||||||
@@ -271,7 +74,7 @@ void _runJsIsolate(Map spawnMessage) async {
|
|||||||
}
|
}
|
||||||
if (msgPort != null)
|
if (msgPort != null)
|
||||||
msgPort.send({
|
msgPort.send({
|
||||||
'data': _encodeData(data),
|
'data': encodeData(data),
|
||||||
});
|
});
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
if (msgPort != null)
|
if (msgPort != null)
|
||||||
@@ -389,10 +192,10 @@ class IsolateQjs {
|
|||||||
'flag': evalFlags,
|
'flag': evalFlags,
|
||||||
'port': evaluatePort.sendPort,
|
'port': evaluatePort.sendPort,
|
||||||
});
|
});
|
||||||
var result = await evaluatePort.first;
|
Map result = await evaluatePort.first;
|
||||||
evaluatePort.close();
|
evaluatePort.close();
|
||||||
if (result['error'] == null) {
|
if (result.containsKey('data')) {
|
||||||
return _decodeData(result['data'], sendPort);
|
return decodeData(result['data'], sendPort);
|
||||||
} else
|
} else
|
||||||
throw result['error'];
|
throw result['error'];
|
||||||
}
|
}
|
||||||
|
532
lib/wrapper.dart
532
lib/wrapper.dart
@@ -7,123 +7,20 @@
|
|||||||
*/
|
*/
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
|
import 'dart:isolate';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
import 'ffi.dart';
|
import 'ffi.dart';
|
||||||
import 'isolate.dart';
|
|
||||||
|
|
||||||
class JSRefValue implements JSRef {
|
abstract class JSInvokable {
|
||||||
Pointer val;
|
dynamic invoke(List args, [dynamic thisVal]);
|
||||||
Pointer ctx;
|
|
||||||
JSRefValue(this.ctx, Pointer val) {
|
|
||||||
Pointer rt = jsGetRuntime(ctx);
|
|
||||||
this.val = jsDupValue(ctx, val);
|
|
||||||
runtimeOpaques[rt]?.ref?.add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSRefValue.fromAddress(int ctx, int val) {
|
static dynamic wrap(dynamic func) {
|
||||||
this.ctx = Pointer.fromAddress(ctx);
|
return func is JSInvokable
|
||||||
this.val = Pointer.fromAddress(val);
|
? func
|
||||||
}
|
: func is Function
|
||||||
|
? _DartFunction(func)
|
||||||
@override
|
: func;
|
||||||
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<dynamic> arguments, [dynamic thisVal]) {
|
|
||||||
if (val == null) return;
|
|
||||||
List<Pointer> 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -135,13 +32,316 @@ class JSFunction extends JSRefValue implements QjsInvokable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer jsGetPropertyStr(Pointer ctx, Pointer val, String prop) {
|
class _DartFunction extends JSInvokable {
|
||||||
var jsAtomVal = jsNewString(ctx, prop);
|
Function _func;
|
||||||
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
|
_DartFunction(this._func);
|
||||||
Pointer jsProp = jsGetProperty(ctx, val, jsAtom);
|
|
||||||
jsFreeAtom(ctx, jsAtom);
|
@override
|
||||||
jsFreeValue(ctx, jsAtomVal);
|
invoke(List args, [thisVal]) {
|
||||||
return jsProp;
|
/// 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<dynamic> 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<dynamic> arguments, [dynamic thisVal]) {
|
||||||
|
if (_val == null) return null;
|
||||||
|
List<Pointer> 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<dynamic, dynamic> 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<dynamic, dynamic> 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}) {
|
String parseJSException(Pointer ctx, {Pointer perr}) {
|
||||||
@@ -149,7 +349,7 @@ String parseJSException(Pointer ctx, {Pointer perr}) {
|
|||||||
|
|
||||||
var err = jsToCString(ctx, e);
|
var err = jsToCString(ctx, e);
|
||||||
if (jsValueGetTag(e) == JSTag.OBJECT) {
|
if (jsValueGetTag(e) == JSTag.OBJECT) {
|
||||||
Pointer stack = jsGetPropertyStr(ctx, e, "stack");
|
Pointer stack = jsGetPropertyValue(ctx, e, "stack");
|
||||||
if (jsToBool(ctx, stack) != 0) {
|
if (jsToBool(ctx, stack) != 0) {
|
||||||
err += '\n' + jsToCString(ctx, stack);
|
err += '\n' + jsToCString(ctx, stack);
|
||||||
}
|
}
|
||||||
@@ -179,8 +379,23 @@ void definePropertyValue(
|
|||||||
jsFreeValue(ctx, jsAtomVal);
|
jsFreeValue(ctx, jsAtomVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pointer jsGetPropertyValue(
|
||||||
|
Pointer ctx,
|
||||||
|
Pointer obj,
|
||||||
|
dynamic key, {
|
||||||
|
Map<dynamic, dynamic> 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<dynamic, dynamic> cache}) {
|
Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
||||||
if (val == null) return jsUNDEFINED();
|
if (val == null) return jsUNDEFINED();
|
||||||
|
if (val is JSObject) return jsDupValue(ctx, val._val);
|
||||||
if (val is Future) {
|
if (val is Future) {
|
||||||
var resolvingFunc = allocate<Uint8>(count: sizeOfJSValue * 2);
|
var resolvingFunc = allocate<Uint8>(count: sizeOfJSValue * 2);
|
||||||
var resolvingFunc2 =
|
var resolvingFunc2 =
|
||||||
@@ -214,9 +429,6 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
|||||||
if (cache.containsKey(val)) {
|
if (cache.containsKey(val)) {
|
||||||
return jsDupValue(ctx, cache[val]);
|
return jsDupValue(ctx, cache[val]);
|
||||||
}
|
}
|
||||||
if (val is JSFunction) {
|
|
||||||
return jsDupValue(ctx, val.val);
|
|
||||||
}
|
|
||||||
if (val is List) {
|
if (val is List) {
|
||||||
Pointer ret = jsNewArray(ctx);
|
Pointer ret = jsNewArray(ctx);
|
||||||
cache[val] = ret;
|
cache[val] = ret;
|
||||||
@@ -233,15 +445,17 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
// wrap Function to JSInvokable
|
||||||
|
final valWrap = JSInvokable.wrap(val);
|
||||||
int dartObjectClassId =
|
int dartObjectClassId =
|
||||||
runtimeOpaques[jsGetRuntime(ctx)]?.dartObjectClassId ?? 0;
|
runtimeOpaques[jsGetRuntime(ctx)]?.dartObjectClassId ?? 0;
|
||||||
if (dartObjectClassId == 0) return jsUNDEFINED();
|
if (dartObjectClassId == 0) return jsUNDEFINED();
|
||||||
var dartObject = jsNewObjectClass(
|
var dartObject = jsNewObjectClass(
|
||||||
ctx,
|
ctx,
|
||||||
dartObjectClassId,
|
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);
|
final ret = jsNewCFunction(ctx, dartObject);
|
||||||
jsFreeValue(ctx, dartObject);
|
jsFreeValue(ctx, dartObject);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -268,7 +482,7 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
|||||||
if (dartObjectClassId != 0) {
|
if (dartObjectClassId != 0) {
|
||||||
final dartObject = DartObject.fromAddress(
|
final dartObject = DartObject.fromAddress(
|
||||||
rt, jsGetObjectOpaque(val, dartObjectClassId));
|
rt, jsGetObjectOpaque(val, dartObjectClassId));
|
||||||
if (dartObject != null) return dartObject.obj;
|
if (dartObject != null) return dartObject._obj;
|
||||||
}
|
}
|
||||||
Pointer<IntPtr> psize = allocate<IntPtr>();
|
Pointer<IntPtr> psize = allocate<IntPtr>();
|
||||||
Pointer<Uint8> buf = jsGetArrayBuffer(ctx, psize, val);
|
Pointer<Uint8> buf = jsGetArrayBuffer(ctx, psize, val);
|
||||||
@@ -284,18 +498,30 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
|||||||
if (jsIsFunction(ctx, val) != 0) {
|
if (jsIsFunction(ctx, val) != 0) {
|
||||||
return JSFunction(ctx, val);
|
return JSFunction(ctx, val);
|
||||||
} else if (jsIsPromise(ctx, val) != 0) {
|
} 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) {
|
} else if (jsIsArray(ctx, val) != 0) {
|
||||||
Pointer jslength = jsGetPropertyStr(ctx, val, "length");
|
Pointer jslength = jsGetPropertyValue(ctx, val, "length");
|
||||||
int length = jsToInt64(ctx, jslength);
|
int length = jsToInt64(ctx, jslength);
|
||||||
List<dynamic> ret = [];
|
List<dynamic> ret = [];
|
||||||
cache[valptr] = ret;
|
cache[valptr] = ret;
|
||||||
for (int i = 0; i < length; ++i) {
|
for (int i = 0; i < length; ++i) {
|
||||||
var jsAtomVal = jsNewInt64(ctx, i);
|
var jsProp = jsGetPropertyValue(ctx, val, i);
|
||||||
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
|
|
||||||
var jsProp = jsGetProperty(ctx, val, jsAtom);
|
|
||||||
jsFreeAtom(ctx, jsAtom);
|
|
||||||
jsFreeValue(ctx, jsAtomVal);
|
|
||||||
ret.add(jsToDart(ctx, jsProp, cache: cache));
|
ret.add(jsToDart(ctx, jsProp, cache: cache));
|
||||||
jsFreeValue(ctx, jsProp);
|
jsFreeValue(ctx, jsProp);
|
||||||
}
|
}
|
||||||
@@ -327,43 +553,3 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
|||||||
}
|
}
|
||||||
return null;
|
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;
|
|
||||||
}
|
|
||||||
""",
|
|
||||||
"<future>",
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
name: flutter_qjs
|
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!
|
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
|
homepage: https://github.com/ekibun/flutter_qjs
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
@@ -9,32 +9,46 @@ import 'dart:async';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter_qjs/ffi.dart';
|
|
||||||
import 'package:flutter_qjs/flutter_qjs.dart';
|
import 'package:flutter_qjs/flutter_qjs.dart';
|
||||||
import 'package:flutter_qjs/isolate.dart';
|
import 'package:flutter_qjs/isolate.dart';
|
||||||
|
import 'package:flutter_qjs/ffi.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
dynamic myFunction(String args, {String thisVal}) {
|
dynamic myFunction(String args, {thisVal}) {
|
||||||
return [thisVal, args];
|
return [thisVal, args];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future testEvaluate(qjs) async {
|
Future testEvaluate(qjs) async {
|
||||||
final testWrap = await qjs.evaluate("(a) => a", name: "<testWrap>");
|
final testWrap = await qjs.evaluate(
|
||||||
|
"(a) => a",
|
||||||
|
name: "<testWrap>",
|
||||||
|
);
|
||||||
|
final wrapNull = await testWrap(null);
|
||||||
|
expect(wrapNull, null, reason: "wrap null");
|
||||||
final primities = [0, 1, 0.1, true, false, "str"];
|
final primities = [0, 1, 0.1, true, false, "str"];
|
||||||
final wrapPrimities = await testWrap(primities);
|
final wrapPrimities = await testWrap(primities);
|
||||||
for (var i = 0; i < primities.length; i++) {
|
for (var i = 0; i < primities.length; i++) {
|
||||||
expect(wrapPrimities[i], primities[i], reason: "wrap primities");
|
expect(wrapPrimities[i], primities[i], reason: "wrap primities");
|
||||||
}
|
}
|
||||||
|
final wrapFunction = await testWrap(testWrap);
|
||||||
|
final testEqual = await qjs.evaluate(
|
||||||
|
"(a, b) => a === b",
|
||||||
|
name: "<testEqual>",
|
||||||
|
);
|
||||||
|
expect(await testEqual(wrapFunction, testWrap), true,
|
||||||
|
reason: "wrap function");
|
||||||
|
|
||||||
|
expect(wrapNull, null, reason: "wrap null");
|
||||||
final a = {};
|
final a = {};
|
||||||
a["a"] = a;
|
a["a"] = a;
|
||||||
final wrapA = await testWrap(a);
|
final wrapA = await testWrap(a);
|
||||||
expect(wrapA['a'], wrapA, reason: "recursive object");
|
expect(wrapA['a'], wrapA, reason: "recursive object");
|
||||||
final testThis = await qjs.evaluate(
|
final testThis = await qjs.evaluate(
|
||||||
"(func) => func.call('this', 'arg')",
|
"(function (func, arg) { return func.call(this, arg) })",
|
||||||
name: "<testThis>",
|
name: "<testThis>",
|
||||||
);
|
);
|
||||||
final funcRet = await testThis(myFunction);
|
final funcRet = await testThis(myFunction, 'arg', thisVal: {'name': 'this'});
|
||||||
expect(funcRet[0], 'this', reason: "js function this");
|
expect(funcRet[0]['name'], 'this', reason: "js function this");
|
||||||
expect(funcRet[1], 'arg', reason: "js function argument");
|
expect(funcRet[1], 'arg', reason: "js function argument");
|
||||||
final promises = await testWrap(await qjs.evaluate(
|
final promises = await testWrap(await qjs.evaluate(
|
||||||
"[Promise.reject('test Promise.reject'), Promise.resolve('test Promise.resolve')]",
|
"[Promise.reject('test Promise.reject'), Promise.resolve('test Promise.resolve')]",
|
||||||
|
Reference in New Issue
Block a user