mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 13:27:24 +00:00
windows wrapper
This commit is contained in:
153
lib/ffi.dart
153
lib/ffi.dart
@@ -3,9 +3,11 @@
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-09-19 10:29:04
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-09-20 15:41:02
|
||||
* @LastEditTime: 2020-09-21 01:30:41
|
||||
*/
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
@@ -24,7 +26,7 @@ class JSProp {
|
||||
static const WRITABLE = (1 << 1);
|
||||
static const ENUMERABLE = (1 << 2);
|
||||
static const C_W_E = (CONFIGURABLE | WRITABLE | ENUMERABLE);
|
||||
}
|
||||
}
|
||||
|
||||
class JSTag {
|
||||
static const FIRST = -11; /* first negative tag */
|
||||
@@ -47,7 +49,11 @@ class JSTag {
|
||||
static const FLOAT64 = 7;
|
||||
}
|
||||
|
||||
final DynamicLibrary qjsLib = DynamicLibrary.open("test/lib/build/Debug/ffi_library.dll");
|
||||
final DynamicLibrary qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
|
||||
? (Platform.isWindows
|
||||
? DynamicLibrary.open("test/build/Debug/flutter_qjs.dll")
|
||||
: DynamicLibrary.process())
|
||||
: (Platform.isWindows ? DynamicLibrary.open("flutter_qjs_plugin.dll") : DynamicLibrary.process());
|
||||
|
||||
/// JSValue *jsEXCEPTION()
|
||||
final Pointer Function() jsEXCEPTION =
|
||||
@@ -68,24 +74,28 @@ final Pointer Function(
|
||||
)>>("jsNewRuntime")
|
||||
.asFunction();
|
||||
|
||||
typedef JSChannel = Pointer Function(Pointer ctx, String method, Pointer argv);
|
||||
typedef JSChannel = Pointer Function(Pointer ctx, Pointer method, Pointer argv);
|
||||
|
||||
class RuntimeOpaque {
|
||||
JSChannel channel;
|
||||
List<JSRef> ref = List();
|
||||
ReceivePort port;
|
||||
Pointer Function(Future) futureToPromise;
|
||||
Future Function(Pointer) promsieToFuture;
|
||||
}
|
||||
|
||||
final Map<Pointer, RuntimeOpaque> runtimeOpaques = Map();
|
||||
|
||||
Pointer channelDispacher(Pointer ctx, Pointer<Utf8> method, Pointer argv) {
|
||||
return runtimeOpaques[jsGetRuntime(ctx)].channel(ctx, Utf8.fromUtf8(method), argv);
|
||||
Pointer channelDispacher(Pointer ctx, Pointer method, Pointer argv) {
|
||||
return runtimeOpaques[jsGetRuntime(ctx)].channel(ctx, method, argv);
|
||||
}
|
||||
|
||||
Pointer jsNewRuntime(
|
||||
JSChannel callback,
|
||||
ReceivePort port,
|
||||
) {
|
||||
var rt = _jsNewRuntime(Pointer.fromFunction(channelDispacher));
|
||||
runtimeOpaques[rt] = RuntimeOpaque()..channel = callback;
|
||||
runtimeOpaques[rt] = RuntimeOpaque()..channel = callback..port = port;
|
||||
return rt;
|
||||
}
|
||||
|
||||
@@ -173,6 +183,7 @@ Pointer jsEval(
|
||||
var val = _jsEval(ctx, utf8input, Utf8.strlen(utf8input), utf8filename, evalFlags);
|
||||
free(utf8input);
|
||||
free(utf8filename);
|
||||
runtimeOpaques[jsGetRuntime(ctx)].port.sendPort.send('eval');
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -503,23 +514,18 @@ final Pointer Function(
|
||||
|
||||
/// int jsDefinePropertyValue(JSContext *ctx, JSValueConst *this_obj,
|
||||
/// JSAtom prop, JSValue *val, int flags)
|
||||
final int Function(
|
||||
Pointer ctx,
|
||||
Pointer thisObj,
|
||||
int prop,
|
||||
Pointer val,
|
||||
int flag
|
||||
) jsDefinePropertyValue = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Int32 Function(
|
||||
Pointer,
|
||||
Pointer,
|
||||
Uint32,
|
||||
Pointer,
|
||||
Int32,
|
||||
)>>("jsDefinePropertyValue")
|
||||
.asFunction();
|
||||
final int Function(Pointer ctx, Pointer thisObj, int prop, Pointer val, int flag)
|
||||
jsDefinePropertyValue = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Int32 Function(
|
||||
Pointer,
|
||||
Pointer,
|
||||
Uint32,
|
||||
Pointer,
|
||||
Int32,
|
||||
)>>("jsDefinePropertyValue")
|
||||
.asFunction();
|
||||
|
||||
/// void jsFreeAtom(JSContext *ctx, JSAtom v)
|
||||
final Pointer Function(
|
||||
@@ -592,3 +598,102 @@ final int Function(
|
||||
Int32,
|
||||
)>>("jsPropertyEnumGetAtom")
|
||||
.asFunction();
|
||||
|
||||
/// uint32_t sizeOfJSValue()
|
||||
final int Function() _sizeOfJSValue =
|
||||
qjsLib.lookup<NativeFunction<Uint32 Function()>>("sizeOfJSValue").asFunction();
|
||||
|
||||
final sizeOfJSValue = _sizeOfJSValue();
|
||||
|
||||
/// void setJSValueList(JSValue *list, int i, JSValue *val)
|
||||
final void Function(
|
||||
Pointer list,
|
||||
int i,
|
||||
Pointer val,
|
||||
) setJSValueList = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Void Function(
|
||||
Pointer,
|
||||
Uint32,
|
||||
Pointer,
|
||||
)>>("setJSValueList")
|
||||
.asFunction();
|
||||
|
||||
/// JSValue *jsCall(JSContext *ctx, JSValueConst *func_obj, JSValueConst *this_obj,
|
||||
/// int argc, JSValueConst *argv)
|
||||
final Pointer Function(
|
||||
Pointer ctx,
|
||||
Pointer funcObj,
|
||||
Pointer thisObj,
|
||||
int argc,
|
||||
Pointer argv,
|
||||
) _jsCall = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Pointer Function(
|
||||
Pointer,
|
||||
Pointer,
|
||||
Pointer,
|
||||
Int32,
|
||||
Pointer,
|
||||
)>>("jsCall")
|
||||
.asFunction();
|
||||
|
||||
Pointer jsCall(
|
||||
Pointer ctx,
|
||||
Pointer funcObj,
|
||||
Pointer thisObj,
|
||||
List<Pointer> argv,
|
||||
) {
|
||||
Pointer jsArgs = allocate<Uint8>(count: argv.length > 0 ? sizeOfJSValue * argv.length : 1);
|
||||
for (int i = 0; i < argv.length; ++i) {
|
||||
Pointer jsArg = argv[i];
|
||||
setJSValueList(jsArgs, i, jsArg);
|
||||
}
|
||||
Pointer func1 = jsDupValue(ctx, funcObj);
|
||||
Pointer _thisObj = thisObj ?? jsUNDEFINED();
|
||||
Pointer jsRet = _jsCall(ctx, funcObj, _thisObj, argv.length, jsArgs);
|
||||
if (thisObj == null) {
|
||||
jsFreeValue(ctx, _thisObj);
|
||||
deleteJSValue(_thisObj);
|
||||
}
|
||||
jsFreeValue(ctx, func1);
|
||||
deleteJSValue(func1);
|
||||
free(jsArgs);
|
||||
runtimeOpaques[jsGetRuntime(ctx)].port.sendPort.send('call');
|
||||
return jsRet;
|
||||
}
|
||||
|
||||
/// int jsIsException(JSValueConst *val)
|
||||
final int Function(
|
||||
Pointer val,
|
||||
) jsIsException = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Int32 Function(
|
||||
Pointer,
|
||||
)>>("jsIsException")
|
||||
.asFunction();
|
||||
|
||||
/// JSValue *jsGetException(JSContext *ctx)
|
||||
final Pointer Function(
|
||||
Pointer ctx,
|
||||
) jsGetException = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Pointer Function(
|
||||
Pointer,
|
||||
)>>("jsGetException")
|
||||
.asFunction();
|
||||
|
||||
/// int jsExecutePendingJob(JSRuntime *rt)
|
||||
final int Function(
|
||||
Pointer ctx,
|
||||
) jsExecutePendingJob = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Int32 Function(
|
||||
Pointer,
|
||||
)>>("jsExecutePendingJob")
|
||||
.asFunction();
|
@@ -3,131 +3,106 @@
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-08 08:29:09
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-09-06 13:03:56
|
||||
* @LastEditTime: 2020-09-21 01:36:30
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:ffi';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter_qjs/ffi.dart';
|
||||
import 'package:flutter_qjs/wrapper.dart';
|
||||
|
||||
/// Handle function to manage js call with `dart(method, ...args)` function.
|
||||
typedef JsMethodHandler = Future<dynamic> Function(String method, List args);
|
||||
typedef JsMethodHandler = dynamic Function(String method, List args);
|
||||
|
||||
/// Handle function to manage js module.
|
||||
typedef JsModuleHandler = Future<String> Function(String name);
|
||||
typedef JsModuleHandler = String Function(String name);
|
||||
|
||||
/// return this in [JsMethodHandler] to mark method not implemented.
|
||||
class JsMethodHandlerNotImplement {}
|
||||
|
||||
/// FlutterJs instance.
|
||||
/// Each [FlutterQjs] object creates a new thread that runs a simple js loop.
|
||||
/// Make sure call `destroy` to terminate thread and release memory when you don't need it.
|
||||
class FlutterQjs {
|
||||
dynamic _engine;
|
||||
dynamic get pointer => _engine;
|
||||
Pointer _rt;
|
||||
Pointer _ctx;
|
||||
ReceivePort port = ReceivePort();
|
||||
JsMethodHandler methodHandler;
|
||||
JsModuleHandler moduleHandler;
|
||||
|
||||
_ensureEngine() async {
|
||||
if (_engine == null) {
|
||||
_engine = await _FlutterJs.instance._channel.invokeMethod("createEngine");
|
||||
}
|
||||
_ensureEngine() {
|
||||
if (_rt != null) return;
|
||||
_rt = jsNewRuntime((ctx, method, argv) {
|
||||
if (method.address != 0) {
|
||||
var argvs = jsToDart(ctx, argv);
|
||||
if (methodHandler == null) throw Exception("No MethodHandler");
|
||||
return dartToJs(ctx, methodHandler(Utf8.fromUtf8(method.cast<Utf8>()), argvs));
|
||||
}
|
||||
if (moduleHandler == null) throw Exception("No ModuleHandler");
|
||||
var ret = Utf8.toUtf8(moduleHandler(Utf8.fromUtf8(argv.cast<Utf8>())));
|
||||
Future.microtask(() {
|
||||
free(ret);
|
||||
});
|
||||
return ret;
|
||||
}, port);
|
||||
_ctx = jsNewContextWithPromsieWrapper(_rt);
|
||||
}
|
||||
|
||||
/// Set a handler to manage js call with `dart(method, ...args)` function.
|
||||
setMethodHandler(JsMethodHandler handler) async {
|
||||
if (handler == null)
|
||||
return _FlutterJs.instance._methodHandlers.remove(_engine);
|
||||
await _ensureEngine();
|
||||
_FlutterJs.instance._methodHandlers[_engine] = handler;
|
||||
setMethodHandler(JsMethodHandler handler) {
|
||||
methodHandler = handler;
|
||||
}
|
||||
|
||||
/// Set a handler to manage js module.
|
||||
setModuleHandler(JsModuleHandler handler) async {
|
||||
if (handler == null)
|
||||
return _FlutterJs.instance._moduleHandlers.remove(_engine);
|
||||
await _ensureEngine();
|
||||
_FlutterJs.instance._moduleHandlers[_engine] = handler;
|
||||
setModuleHandler(JsModuleHandler handler) {
|
||||
moduleHandler = handler;
|
||||
}
|
||||
|
||||
/// Terminate thread and release memory.
|
||||
destroy() async {
|
||||
if (_engine != null) {
|
||||
await setMethodHandler(null);
|
||||
await setModuleHandler(null);
|
||||
var engine = _engine;
|
||||
_engine = null;
|
||||
await _FlutterJs.instance._channel.invokeMethod("close", engine);
|
||||
/// Free Runtime and Context which can be recreate when evaluate again.
|
||||
recreate() {
|
||||
if (_rt != null) {
|
||||
jsFreeContext(_ctx);
|
||||
jsFreeRuntime(_rt);
|
||||
}
|
||||
_rt = null;
|
||||
_ctx = null;
|
||||
}
|
||||
|
||||
/// Close ReceivePort.
|
||||
close() {
|
||||
if (port != null) {
|
||||
port.close();
|
||||
recreate();
|
||||
}
|
||||
port = null;
|
||||
}
|
||||
|
||||
/// DispatchMessage
|
||||
Future<void> dispatch() async {
|
||||
await for (var _ in port) {
|
||||
while (true) {
|
||||
int err = jsExecutePendingJob(_rt);
|
||||
if (err <= 0) {
|
||||
if (err < 0) print(parseJSException(_ctx));
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate js script.
|
||||
Future<dynamic> evaluate(String command, String name) async {
|
||||
await _ensureEngine();
|
||||
var arguments = {"engine": _engine, "script": command, "name": name};
|
||||
return _FlutterJs.instance._wrapFunctionArguments(
|
||||
await _FlutterJs.instance._channel.invokeMethod("evaluate", arguments),
|
||||
_engine);
|
||||
}
|
||||
}
|
||||
|
||||
class _FlutterJs {
|
||||
factory _FlutterJs() => _getInstance();
|
||||
static _FlutterJs get instance => _getInstance();
|
||||
static _FlutterJs _instance;
|
||||
MethodChannel _channel = const MethodChannel('soko.ekibun.flutter_qjs');
|
||||
Map<dynamic, JsMethodHandler> _methodHandlers =
|
||||
Map<dynamic, JsMethodHandler>();
|
||||
Map<dynamic, JsModuleHandler> _moduleHandlers =
|
||||
Map<dynamic, JsModuleHandler>();
|
||||
_FlutterJs._internal() {
|
||||
_channel.setMethodCallHandler((call) async {
|
||||
var engine = call.arguments["engine"];
|
||||
var args = call.arguments["args"];
|
||||
if (args is List) {
|
||||
if (_methodHandlers[engine] == null) return call.noSuchMethod(null);
|
||||
var ret = await _methodHandlers[engine](
|
||||
call.method, _wrapFunctionArguments(args, engine));
|
||||
if (ret is JsMethodHandlerNotImplement) return call.noSuchMethod(null);
|
||||
return ret;
|
||||
} else {
|
||||
if (_moduleHandlers[engine] == null) return call.noSuchMethod(null);
|
||||
var ret = await _moduleHandlers[engine](args);
|
||||
if (ret is JsMethodHandlerNotImplement) return call.noSuchMethod(null);
|
||||
return ret;
|
||||
}
|
||||
});
|
||||
}
|
||||
dynamic _wrapFunctionArguments(dynamic val, dynamic engine) {
|
||||
if (val is List && !(val is List<int>)) {
|
||||
for (var i = 0; i < val.length; ++i) {
|
||||
val[i] = _wrapFunctionArguments(val[i], engine);
|
||||
}
|
||||
} else if (val is Map) {
|
||||
// wrap boolean in Android see https://github.com/flutter/flutter/issues/45066
|
||||
if (Platform.isAndroid && val["__js_boolean__"] != null) {
|
||||
return val["__js_boolean__"] != 0;
|
||||
}
|
||||
if (val["__js_function__"] != null) {
|
||||
var functionId = val["__js_function__"];
|
||||
return (List<dynamic> args) async {
|
||||
var arguments = {
|
||||
"engine": engine,
|
||||
"function": functionId,
|
||||
"arguments": args,
|
||||
};
|
||||
return _wrapFunctionArguments(
|
||||
await _channel.invokeMethod("call", arguments), engine);
|
||||
};
|
||||
} else
|
||||
for (var key in val.keys) {
|
||||
val[key] = _wrapFunctionArguments(val[key], engine);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static _FlutterJs _getInstance() {
|
||||
if (_instance == null) {
|
||||
_instance = new _FlutterJs._internal();
|
||||
}
|
||||
return _instance;
|
||||
_ensureEngine();
|
||||
var jsval = jsEval(_ctx, command, name, JSEvalType.GLOBAL);
|
||||
if (jsIsException(jsval) != 0) {
|
||||
throw Exception(parseJSException(_ctx));
|
||||
}
|
||||
var ret = runtimeOpaques[_rt]?.promsieToFuture(jsval);
|
||||
jsFreeValue(_ctx, jsval);
|
||||
deleteJSValue(jsval);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
173
lib/wrapper.dart
173
lib/wrapper.dart
@@ -3,8 +3,9 @@
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-09-19 22:07:47
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-09-20 15:41:16
|
||||
* @LastEditTime: 2020-09-21 01:23:06
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
@@ -12,10 +13,10 @@ import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'ffi.dart';
|
||||
|
||||
class JSFunction extends JSRef {
|
||||
class JSRefValue implements JSRef {
|
||||
Pointer val;
|
||||
Pointer ctx;
|
||||
JSFunction(this.ctx, Pointer val) {
|
||||
JSRefValue(this.ctx, Pointer val) {
|
||||
Pointer rt = jsGetRuntime(ctx);
|
||||
this.val = jsDupValue(ctx, val);
|
||||
runtimeOpaques[rt]?.ref?.add(this);
|
||||
@@ -26,17 +27,92 @@ class JSFunction extends JSRef {
|
||||
if (val != null) {
|
||||
jsFreeValue(ctx, val);
|
||||
deleteJSValue(val);
|
||||
val = null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) {
|
||||
return super.noSuchMethod(invocation);
|
||||
val = 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) {
|
||||
completer.completeError(status["__error"] ?? "undefined");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class JSFunction extends JSRefValue {
|
||||
JSFunction(Pointer ctx, Pointer val) : super(ctx, val);
|
||||
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) {
|
||||
if (val == null) return;
|
||||
List<Pointer> args = invocation.positionalArguments.map((e) => dartToJs(ctx, e)).toList();
|
||||
Pointer jsRet = jsCall(ctx, val, null, args);
|
||||
for (Pointer jsArg in args) {
|
||||
jsFreeValue(ctx, jsArg);
|
||||
deleteJSValue(jsArg);
|
||||
}
|
||||
bool isException = jsIsException(jsRet) != 0;
|
||||
var ret = jsToDart(ctx, jsRet);
|
||||
jsFreeValue(ctx, jsRet);
|
||||
deleteJSValue(jsRet);
|
||||
if (isException) {
|
||||
throw Exception(parseJSException(ctx));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
deleteJSValue(jsAtomVal);
|
||||
return jsProp;
|
||||
}
|
||||
|
||||
String parseJSException(Pointer ctx) {
|
||||
Pointer e = jsGetException(ctx);
|
||||
var err = jsToCString(ctx, e);
|
||||
if (jsValueGetTag(e) == JSTag.OBJECT) {
|
||||
Pointer stack = jsGetPropertyStr(ctx, e, "stack");
|
||||
if (jsToBool(ctx, stack) != 0) {
|
||||
err += '\n' + jsToCString(ctx, stack);
|
||||
}
|
||||
jsFreeValue(ctx, stack);
|
||||
deleteJSValue(stack);
|
||||
}
|
||||
jsFreeValue(ctx, e);
|
||||
deleteJSValue(e);
|
||||
return err;
|
||||
}
|
||||
|
||||
Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
||||
if (val is Future) {
|
||||
return runtimeOpaques[jsGetRuntime(ctx)]?.futureToPromise(val);
|
||||
}
|
||||
if (cache == null) cache = Map();
|
||||
if (val is bool) return jsNewBool(ctx, val ? 1 : 0);
|
||||
if (val is int) return jsNewInt64(ctx, val);
|
||||
@@ -51,7 +127,7 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
||||
return ret;
|
||||
}
|
||||
if (cache.containsKey(val)) {
|
||||
return cache[val];
|
||||
return jsDupValue(ctx, cache[val]);
|
||||
}
|
||||
if (val is JSFunction) {
|
||||
return jsDupValue(ctx, val.val);
|
||||
@@ -78,7 +154,7 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
||||
if (val is Map) {
|
||||
Pointer ret = jsNewObject(ctx);
|
||||
cache[val] = ret;
|
||||
for (MapEntry<dynamic, dynamic> entry in val.entries){
|
||||
for (MapEntry<dynamic, dynamic> entry in val.entries) {
|
||||
var jsAtomVal = dartToJs(ctx, entry.key, cache: cache);
|
||||
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
|
||||
jsDefinePropertyValue(
|
||||
@@ -125,13 +201,7 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
||||
if (jsIsFunction(ctx, val) != 0) {
|
||||
return JSFunction(ctx, val);
|
||||
} else if (jsIsArray(ctx, val) != 0) {
|
||||
var jsAtomVal = jsNewString(ctx, "length");
|
||||
var jsAtom = jsValueToAtom(ctx, jsAtomVal);
|
||||
var jslength = jsGetProperty(ctx, val, jsAtom);
|
||||
jsFreeAtom(ctx, jsAtom);
|
||||
jsFreeValue(ctx, jsAtomVal);
|
||||
deleteJSValue(jsAtomVal);
|
||||
|
||||
Pointer jslength = jsGetPropertyStr(ctx, val, "length");
|
||||
int length = jsToInt64(ctx, jslength);
|
||||
deleteJSValue(jslength);
|
||||
List<dynamic> ret = List();
|
||||
@@ -175,3 +245,70 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Pointer jsNewContextWithPromsieWrapper(Pointer rt) {
|
||||
var ctx = jsNewContext(rt);
|
||||
var jsPromiseCtor = jsEval(
|
||||
ctx,
|
||||
"""
|
||||
() => {
|
||||
const __resolver = {};
|
||||
const __ret = new Promise((res, rej) => {
|
||||
__resolver.__res = res;
|
||||
__resolver.__rej = rej;
|
||||
});
|
||||
__ret.__res = __resolver.__res;
|
||||
__ret.__rej = __resolver.__rej;
|
||||
return __ret;
|
||||
}
|
||||
""",
|
||||
"<future>",
|
||||
JSEvalType.GLOBAL);
|
||||
var promiseCtor = JSRefValue(ctx, jsPromiseCtor);
|
||||
jsFreeValue(ctx, jsPromiseCtor);
|
||||
deleteJSValue(jsPromiseCtor);
|
||||
runtimeOpaques[rt].futureToPromise = (future) {
|
||||
var ctor = promiseCtor.val;
|
||||
if (ctor == null) throw Exception("Runtime has been released!");
|
||||
var jsPromise = jsCall(ctx, ctor, null, List());
|
||||
var promise = jsToDart(ctx, jsPromise);
|
||||
future.then((value) {
|
||||
promise['__res'](value);
|
||||
}).catchError((err) {
|
||||
promise['__rej'](err);
|
||||
});
|
||||
return jsPromise;
|
||||
};
|
||||
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>",
|
||||
JSEvalType.GLOBAL);
|
||||
var promiseWrapper = JSRefValue(ctx, jsPromiseWrapper);
|
||||
jsFreeValue(ctx, jsPromiseWrapper);
|
||||
deleteJSValue(jsPromiseWrapper);
|
||||
runtimeOpaques[rt].promsieToFuture = (promise) {
|
||||
var completer = Completer();
|
||||
var wrapper = promiseWrapper.val;
|
||||
if (wrapper == null) completer.completeError(Exception("Runtime has been released!"));
|
||||
var jsPromise = jsCall(ctx, wrapper, null, [promise]);
|
||||
runtimeOpaques[rt].ref.add(JSPromise(ctx, jsPromise, completer));
|
||||
jsFreeValue(ctx, jsPromise);
|
||||
deleteJSValue(jsPromise);
|
||||
return completer.future;
|
||||
};
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
Reference in New Issue
Block a user