mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 05:27:23 +00:00
nullsafety
This commit is contained in:
@@ -15,20 +15,20 @@ typedef _JsHostPromiseRejectionHandler = void Function(dynamic reason);
|
||||
|
||||
/// Quickjs engine for flutter.
|
||||
class FlutterQjs {
|
||||
Pointer<JSRuntime> _rt;
|
||||
Pointer<JSContext> _ctx;
|
||||
Pointer<JSRuntime>? _rt;
|
||||
Pointer<JSContext>? _ctx;
|
||||
|
||||
/// Max stack size for quickjs.
|
||||
final int stackSize;
|
||||
final int? stackSize;
|
||||
|
||||
/// Message Port for event loop. Close it to stop dispatching event loop.
|
||||
ReceivePort port = ReceivePort();
|
||||
|
||||
/// Handler function to manage js module.
|
||||
_JsModuleHandler moduleHandler;
|
||||
final _JsModuleHandler? moduleHandler;
|
||||
|
||||
/// Handler function to manage js module.
|
||||
_JsHostPromiseRejectionHandler hostPromiseRejectionHandler;
|
||||
final _JsHostPromiseRejectionHandler? hostPromiseRejectionHandler;
|
||||
|
||||
FlutterQjs({
|
||||
this.moduleHandler,
|
||||
@@ -38,7 +38,7 @@ class FlutterQjs {
|
||||
|
||||
_ensureEngine() {
|
||||
if (_rt != null) return;
|
||||
_rt = jsNewRuntime((ctx, type, ptr) {
|
||||
final rt = jsNewRuntime((ctx, type, ptr) {
|
||||
try {
|
||||
switch (type) {
|
||||
case JSChannelType.METHON:
|
||||
@@ -65,7 +65,7 @@ class FlutterQjs {
|
||||
));
|
||||
case JSChannelType.MODULE:
|
||||
if (moduleHandler == null) throw JSError('No ModuleHandler');
|
||||
final ret = moduleHandler(
|
||||
final ret = moduleHandler!(
|
||||
ptr.cast<Utf8>().toDartString(),
|
||||
).toNativeUtf8();
|
||||
Future.microtask(() {
|
||||
@@ -75,15 +75,14 @@ class FlutterQjs {
|
||||
case JSChannelType.PROMISE_TRACK:
|
||||
final err = _parseJSException(ctx, ptr);
|
||||
if (hostPromiseRejectionHandler != null) {
|
||||
hostPromiseRejectionHandler(err);
|
||||
hostPromiseRejectionHandler!(err);
|
||||
} else {
|
||||
print('unhandled promise rejection: $err');
|
||||
}
|
||||
return nullptr;
|
||||
case JSChannelType.FREE_OBJECT:
|
||||
final rt = ctx.cast<JSRuntime>();
|
||||
_DartObject obj = _DartObject.fromAddress(rt, ptr.address);
|
||||
obj?.free();
|
||||
_DartObject.fromAddress(rt, ptr.address)?.free();
|
||||
return nullptr;
|
||||
}
|
||||
throw JSError('call channel with wrong type');
|
||||
@@ -106,20 +105,21 @@ class FlutterQjs {
|
||||
return err;
|
||||
}
|
||||
}, port);
|
||||
if (this.stackSize != null && this.stackSize > 0)
|
||||
jsSetMaxStackSize(_rt, this.stackSize);
|
||||
_ctx = jsNewContext(_rt);
|
||||
final stackSize = this.stackSize ?? 0;
|
||||
if (stackSize > 0) jsSetMaxStackSize(rt, stackSize);
|
||||
_rt = rt;
|
||||
_ctx = jsNewContext(rt);
|
||||
}
|
||||
|
||||
/// Free Runtime and Context which can be recreate when evaluate again.
|
||||
close() {
|
||||
if (_rt == null) return;
|
||||
final rt = _rt;
|
||||
final ctx = _ctx;
|
||||
_executePendingJob();
|
||||
_rt = null;
|
||||
_ctx = null;
|
||||
jsFreeContext(ctx);
|
||||
if (ctx != null) jsFreeContext(ctx);
|
||||
if (rt == null) return;
|
||||
_executePendingJob();
|
||||
try {
|
||||
jsFreeRuntime(rt);
|
||||
} on String catch (e) {
|
||||
@@ -128,11 +128,13 @@ class FlutterQjs {
|
||||
}
|
||||
|
||||
void _executePendingJob() {
|
||||
if (_rt == null) return;
|
||||
final rt = _rt;
|
||||
final ctx = _ctx;
|
||||
if (rt == null || ctx == null) return;
|
||||
while (true) {
|
||||
int err = jsExecutePendingJob(_rt);
|
||||
int err = jsExecutePendingJob(rt);
|
||||
if (err <= 0) {
|
||||
if (err < 0) print(_parseJSException(_ctx));
|
||||
if (err < 0) print(_parseJSException(ctx));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -146,20 +148,25 @@ class FlutterQjs {
|
||||
}
|
||||
|
||||
/// Evaluate js script.
|
||||
dynamic evaluate(String command, {String name, int evalFlags}) {
|
||||
dynamic evaluate(
|
||||
String command, {
|
||||
String? name,
|
||||
int? evalFlags,
|
||||
}) {
|
||||
_ensureEngine();
|
||||
final ctx = _ctx!;
|
||||
final jsval = jsEval(
|
||||
_ctx,
|
||||
ctx,
|
||||
command,
|
||||
name ?? '<eval>',
|
||||
evalFlags ?? JSEvalFlag.GLOBAL,
|
||||
);
|
||||
if (jsIsException(jsval) != 0) {
|
||||
jsFreeValue(_ctx, jsval);
|
||||
throw _parseJSException(_ctx);
|
||||
jsFreeValue(ctx, jsval);
|
||||
throw _parseJSException(ctx);
|
||||
}
|
||||
final result = _jsToDart(_ctx, jsval);
|
||||
jsFreeValue(_ctx, jsval);
|
||||
final result = _jsToDart(ctx, jsval);
|
||||
jsFreeValue(ctx, jsval);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,16 @@ import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
extension ListFirstWhere<T> on Iterable<T> {
|
||||
T? firstWhereOrNull(bool Function(T) test) {
|
||||
try {
|
||||
return firstWhere(test);
|
||||
} on StateError {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class JSRef {
|
||||
int _refCount = 0;
|
||||
void dup() {
|
||||
@@ -34,7 +44,7 @@ abstract class JSRef {
|
||||
static void _callRecursive(
|
||||
dynamic obj,
|
||||
void Function(JSRef) cb, [
|
||||
Set cache,
|
||||
Set? cache,
|
||||
]) {
|
||||
if (obj == null) return;
|
||||
if (cache == null) cache = Set();
|
||||
@@ -155,24 +165,26 @@ final Pointer<JSRuntime> Function(
|
||||
.asFunction();
|
||||
|
||||
class _RuntimeOpaque {
|
||||
_JSChannel _channel;
|
||||
final _JSChannel _channel;
|
||||
List<JSRef> _ref = [];
|
||||
ReceivePort _port;
|
||||
int _dartObjectClassId;
|
||||
get dartObjectClassId => _dartObjectClassId;
|
||||
final ReceivePort _port;
|
||||
int? _dartObjectClassId;
|
||||
_RuntimeOpaque(this._channel, this._port);
|
||||
|
||||
int? get dartObjectClassId => _dartObjectClassId;
|
||||
|
||||
void addRef(JSRef ref) => _ref.add(ref);
|
||||
|
||||
bool removeRef(JSRef ref) => _ref.remove(ref);
|
||||
|
||||
JSRef getRef(bool Function(JSRef ref) test) {
|
||||
return _ref.firstWhere(test, orElse: () => null);
|
||||
JSRef? getRef(bool Function(JSRef ref) test) {
|
||||
return _ref.firstWhereOrNull(test);
|
||||
}
|
||||
}
|
||||
|
||||
final Map<Pointer<JSRuntime>, _RuntimeOpaque> runtimeOpaques = Map();
|
||||
|
||||
Pointer<JSValue> channelDispacher(
|
||||
Pointer<JSValue>? channelDispacher(
|
||||
Pointer<JSContext> ctx,
|
||||
int type,
|
||||
Pointer<JSValue> argv,
|
||||
@@ -188,9 +200,7 @@ Pointer<JSRuntime> jsNewRuntime(
|
||||
ReceivePort port,
|
||||
) {
|
||||
final rt = _jsNewRuntime(Pointer.fromFunction(channelDispacher));
|
||||
runtimeOpaques[rt] = _RuntimeOpaque()
|
||||
.._channel = callback
|
||||
.._port = port;
|
||||
runtimeOpaques[rt] = _RuntimeOpaque(callback, port);
|
||||
return rt;
|
||||
}
|
||||
|
||||
@@ -222,21 +232,22 @@ void jsFreeRuntime(
|
||||
Pointer<JSRuntime> rt,
|
||||
) {
|
||||
final referenceleak = <String>[];
|
||||
while (true) {
|
||||
final ref = runtimeOpaques[rt]
|
||||
?._ref
|
||||
?.firstWhere((ref) => ref is JSRefLeakable, orElse: () => null);
|
||||
if (ref == null) break;
|
||||
ref.destroy();
|
||||
runtimeOpaques[rt]?._ref?.remove(ref);
|
||||
}
|
||||
while (0 < runtimeOpaques[rt]?._ref?.length ?? 0) {
|
||||
final ref = runtimeOpaques[rt]?._ref?.first;
|
||||
final objStrs = ref.toString().split('\n');
|
||||
final objStr = objStrs.length > 0 ? objStrs[0] + " ..." : objStrs[0];
|
||||
referenceleak.add(
|
||||
" ${identityHashCode(ref)}\t${ref._refCount + 1}\t${ref.runtimeType.toString()}\t$objStr");
|
||||
ref.destroy();
|
||||
final opaque = runtimeOpaques[rt];
|
||||
if (opaque != null) {
|
||||
while (true) {
|
||||
final ref = opaque._ref.firstWhereOrNull((ref) => ref is JSRefLeakable);
|
||||
if (ref == null) break;
|
||||
ref.destroy();
|
||||
runtimeOpaques[rt]?._ref.remove(ref);
|
||||
}
|
||||
while (opaque._ref.isNotEmpty) {
|
||||
final ref = opaque._ref.first;
|
||||
final objStrs = ref.toString().split('\n');
|
||||
final objStr = objStrs.length > 0 ? objStrs[0] + " ..." : objStrs[0];
|
||||
referenceleak.add(
|
||||
" ${identityHashCode(ref)}\t${ref._refCount + 1}\t${ref.runtimeType.toString()}\t$objStr");
|
||||
ref.destroy();
|
||||
}
|
||||
}
|
||||
_jsFreeRuntime(rt);
|
||||
if (referenceleak.length > 0) {
|
||||
@@ -335,7 +346,7 @@ Pointer<JSValue> jsEval(
|
||||
);
|
||||
malloc.free(utf8input);
|
||||
malloc.free(utf8filename);
|
||||
runtimeOpaques[jsGetRuntime(ctx)]._port.sendPort.send(#eval);
|
||||
runtimeOpaques[jsGetRuntime(ctx)]?._port.sendPort.send(#eval);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -915,14 +926,11 @@ Pointer<JSValue> jsCall(
|
||||
setJSValueList(jsArgs, i, jsArg);
|
||||
}
|
||||
final func1 = jsDupValue(ctx, funcObj);
|
||||
final _thisObj = thisObj ?? jsUNDEFINED();
|
||||
final _thisObj = thisObj;
|
||||
final jsRet = _jsCall(ctx, funcObj, _thisObj, argv.length, jsArgs);
|
||||
if (thisObj == null) {
|
||||
jsFreeValue(ctx, _thisObj);
|
||||
}
|
||||
jsFreeValue(ctx, func1);
|
||||
malloc.free(jsArgs);
|
||||
runtimeOpaques[jsGetRuntime(ctx)]._port.sendPort.send(#call);
|
||||
runtimeOpaques[jsGetRuntime(ctx)]?._port.sendPort.send(#call);
|
||||
return jsRet;
|
||||
}
|
||||
|
||||
|
@@ -17,7 +17,7 @@ abstract class _IsolateEncodable {
|
||||
Map _encode();
|
||||
}
|
||||
|
||||
dynamic _encodeData(dynamic data, {Map<dynamic, dynamic> cache}) {
|
||||
dynamic _encodeData(dynamic data, {Map<dynamic, dynamic>? cache}) {
|
||||
if (cache == null) cache = Map();
|
||||
if (cache.containsKey(data)) return cache[data];
|
||||
if (data is _IsolateEncodable) return data._encode();
|
||||
@@ -58,7 +58,7 @@ dynamic _encodeData(dynamic data, {Map<dynamic, dynamic> cache}) {
|
||||
return data;
|
||||
}
|
||||
|
||||
dynamic _decodeData(dynamic data, {Map<dynamic, dynamic> cache}) {
|
||||
dynamic _decodeData(dynamic data, {Map<dynamic, dynamic>? cache}) {
|
||||
if (cache == null) cache = Map();
|
||||
if (cache.containsKey(data)) return cache[data];
|
||||
if (data is List) {
|
||||
@@ -132,7 +132,7 @@ void _runJsIsolate(Map spawnMessage) async {
|
||||
);
|
||||
port.listen((msg) async {
|
||||
var data;
|
||||
SendPort msgPort = msg[#port];
|
||||
SendPort? msgPort = msg[#port];
|
||||
try {
|
||||
switch (msg[#type]) {
|
||||
case #evaluate:
|
||||
@@ -164,16 +164,16 @@ void _runJsIsolate(Map spawnMessage) async {
|
||||
typedef _JsAsyncModuleHandler = Future<String> Function(String name);
|
||||
|
||||
class IsolateQjs {
|
||||
Future<SendPort> _sendPort;
|
||||
Future<SendPort>? _sendPort;
|
||||
|
||||
/// Max stack size for quickjs.
|
||||
final int stackSize;
|
||||
final int? stackSize;
|
||||
|
||||
/// Asynchronously handler to manage js module.
|
||||
_JsAsyncModuleHandler moduleHandler;
|
||||
final _JsAsyncModuleHandler? moduleHandler;
|
||||
|
||||
/// Handler function to manage js module.
|
||||
_JsHostPromiseRejectionHandler hostPromiseRejectionHandler;
|
||||
final _JsHostPromiseRejectionHandler? hostPromiseRejectionHandler;
|
||||
|
||||
/// Quickjs engine runing on isolate thread.
|
||||
///
|
||||
@@ -207,7 +207,7 @@ class IsolateQjs {
|
||||
try {
|
||||
final err = _decodeData(msg[#reason]);
|
||||
if (hostPromiseRejectionHandler != null) {
|
||||
hostPromiseRejectionHandler(err);
|
||||
hostPromiseRejectionHandler!(err);
|
||||
} else {
|
||||
print('unhandled promise rejection: $err');
|
||||
}
|
||||
@@ -218,7 +218,7 @@ class IsolateQjs {
|
||||
case #module:
|
||||
final ptr = Pointer<Pointer>.fromAddress(msg[#ptr]);
|
||||
try {
|
||||
ptr.value = (await moduleHandler(msg[#name])).toNativeUtf8();
|
||||
ptr.value = (await moduleHandler!(msg[#name])).toNativeUtf8();
|
||||
} catch (e) {
|
||||
ptr.value = Pointer.fromAddress(-1);
|
||||
}
|
||||
@@ -234,8 +234,10 @@ class IsolateQjs {
|
||||
|
||||
/// Free Runtime and close isolate thread that can be recreate when evaluate again.
|
||||
close() {
|
||||
if (_sendPort == null) return;
|
||||
final ret = _sendPort.then((sendPort) async {
|
||||
final sendPort = _sendPort;
|
||||
_sendPort = null;
|
||||
if (sendPort == null) return;
|
||||
final ret = sendPort.then((sendPort) async {
|
||||
final closePort = ReceivePort();
|
||||
sendPort.send({
|
||||
#type: #close,
|
||||
@@ -247,15 +249,18 @@ class IsolateQjs {
|
||||
throw _decodeData(result[#error]);
|
||||
return _decodeData(result);
|
||||
});
|
||||
_sendPort = null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Evaluate js script.
|
||||
Future<dynamic> evaluate(String command, {String name, int evalFlags}) async {
|
||||
Future<dynamic> evaluate(
|
||||
String command, {
|
||||
String? name,
|
||||
int? evalFlags,
|
||||
}) async {
|
||||
_ensureEngine();
|
||||
final evaluatePort = ReceivePort();
|
||||
final sendPort = await _sendPort;
|
||||
final sendPort = await _sendPort!;
|
||||
sendPort.send({
|
||||
#type: #evaluate,
|
||||
#command: command,
|
||||
|
@@ -48,17 +48,18 @@ class _DartFunction extends JSInvokable {
|
||||
/// implement this to capture js object release.
|
||||
|
||||
class _DartObject extends JSRef implements JSRefLeakable {
|
||||
Object _obj;
|
||||
Pointer<JSContext> _ctx;
|
||||
_DartObject(this._ctx, this._obj) {
|
||||
if (_obj is JSRef) {
|
||||
(_obj as JSRef).dup();
|
||||
}
|
||||
runtimeOpaques[jsGetRuntime(_ctx)]?.addRef(this);
|
||||
Object? _obj;
|
||||
Pointer<JSContext>? _ctx;
|
||||
_DartObject(Pointer<JSContext> ctx, dynamic obj) {
|
||||
_ctx = ctx;
|
||||
_obj = obj;
|
||||
if (obj is JSRef) obj.dup();
|
||||
runtimeOpaques[jsGetRuntime(ctx)]?.addRef(this);
|
||||
}
|
||||
|
||||
static _DartObject fromAddress(Pointer<JSRuntime> rt, int val) {
|
||||
return runtimeOpaques[rt]?.getRef((e) => identityHashCode(e) == val);
|
||||
static _DartObject? fromAddress(Pointer<JSRuntime> rt, int val) {
|
||||
return runtimeOpaques[rt]?.getRef((e) => identityHashCode(e) == val)
|
||||
as _DartObject?;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -69,20 +70,20 @@ class _DartObject extends JSRef implements JSRefLeakable {
|
||||
|
||||
@override
|
||||
void destroy() {
|
||||
if (_ctx == null) return;
|
||||
runtimeOpaques[jsGetRuntime(_ctx)]?.removeRef(this);
|
||||
final ctx = _ctx;
|
||||
final obj = _obj;
|
||||
_ctx = null;
|
||||
if (_obj is JSRef) {
|
||||
(_obj as JSRef).free();
|
||||
}
|
||||
_obj = null;
|
||||
if (ctx == null) return;
|
||||
runtimeOpaques[jsGetRuntime(ctx)]?.removeRef(this);
|
||||
if (obj is JSRef) obj.free();
|
||||
}
|
||||
}
|
||||
|
||||
/// JS Error wrapper
|
||||
class JSError extends _IsolateEncodable {
|
||||
String message;
|
||||
String stack;
|
||||
late String message;
|
||||
late String stack;
|
||||
JSError(message, [stack]) {
|
||||
if (message is JSError) {
|
||||
this.message = message.message;
|
||||
@@ -95,10 +96,10 @@ class JSError extends _IsolateEncodable {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return stack == null ? message.toString() : "$message\n$stack";
|
||||
return stack.isEmpty ? message.toString() : "$message\n$stack";
|
||||
}
|
||||
|
||||
static JSError _decode(Map obj) {
|
||||
static JSError? _decode(Map obj) {
|
||||
if (obj.containsKey(#jsError))
|
||||
return JSError(obj[#jsError], obj[#jsErrorStack]);
|
||||
return null;
|
||||
@@ -116,30 +117,33 @@ class JSError extends _IsolateEncodable {
|
||||
/// JS Object reference
|
||||
/// call [release] to release js object.
|
||||
class _JSObject extends JSRef {
|
||||
Pointer<JSValue> _val;
|
||||
Pointer<JSContext> _ctx;
|
||||
Pointer<JSValue>? _val;
|
||||
Pointer<JSContext>? _ctx;
|
||||
|
||||
/// Create
|
||||
_JSObject(this._ctx, Pointer<JSValue> _val) {
|
||||
final rt = jsGetRuntime(_ctx);
|
||||
this._val = jsDupValue(_ctx, _val);
|
||||
_JSObject(Pointer<JSContext> ctx, Pointer<JSValue> val) {
|
||||
this._ctx = ctx;
|
||||
final rt = jsGetRuntime(ctx);
|
||||
this._val = jsDupValue(ctx, val);
|
||||
runtimeOpaques[rt]?.addRef(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void destroy() {
|
||||
if (_val == null) return;
|
||||
final rt = jsGetRuntime(_ctx);
|
||||
runtimeOpaques[rt]?.removeRef(this);
|
||||
jsFreeValue(_ctx, _val);
|
||||
final ctx = _ctx;
|
||||
final val = _val;
|
||||
_val = null;
|
||||
_ctx = null;
|
||||
if (ctx == null || val == null) return;
|
||||
final rt = jsGetRuntime(ctx);
|
||||
runtimeOpaques[rt]?.removeRef(this);
|
||||
jsFreeValue(ctx, val);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (_val == null) return "JSObject(released)";
|
||||
return jsToCString(_ctx, _val);
|
||||
if (_ctx == null || _val == null) return "JSObject(released)";
|
||||
return jsToCString(_ctx!, _val!);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,29 +154,32 @@ class _JSFunction extends _JSObject implements JSInvokable, _IsolateEncodable {
|
||||
@override
|
||||
invoke(List<dynamic> arguments, [dynamic thisVal]) {
|
||||
final jsRet = _invoke(arguments, thisVal);
|
||||
if (jsRet == null) return;
|
||||
final ctx = _ctx!;
|
||||
bool isException = jsIsException(jsRet) != 0;
|
||||
if (isException) {
|
||||
jsFreeValue(_ctx, jsRet);
|
||||
throw _parseJSException(_ctx);
|
||||
jsFreeValue(ctx, jsRet);
|
||||
throw _parseJSException(ctx);
|
||||
}
|
||||
final ret = _jsToDart(_ctx, jsRet);
|
||||
jsFreeValue(_ctx, jsRet);
|
||||
final ret = _jsToDart(ctx, jsRet);
|
||||
jsFreeValue(ctx, jsRet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Pointer<JSValue> _invoke(List<dynamic> arguments, [dynamic thisVal]) {
|
||||
if (_val == null) throw JSError("InternalError: JSValue released");
|
||||
final ctx = _ctx;
|
||||
final val = _val;
|
||||
if (ctx == null || val == null)
|
||||
throw JSError("InternalError: JSValue released");
|
||||
final args = arguments
|
||||
.map(
|
||||
(e) => _dartToJs(_ctx, e),
|
||||
(e) => _dartToJs(ctx, e),
|
||||
)
|
||||
.toList();
|
||||
final jsThis = _dartToJs(_ctx, thisVal);
|
||||
final jsRet = jsCall(_ctx, _val, jsThis, args);
|
||||
jsFreeValue(_ctx, jsThis);
|
||||
final jsThis = _dartToJs(ctx, thisVal);
|
||||
final jsRet = jsCall(ctx, val, jsThis, args);
|
||||
jsFreeValue(ctx, jsThis);
|
||||
for (final jsArg in args) {
|
||||
jsFreeValue(_ctx, jsArg);
|
||||
jsFreeValue(ctx, jsArg);
|
||||
}
|
||||
return jsRet;
|
||||
}
|
||||
@@ -185,9 +192,9 @@ class _JSFunction extends _JSObject implements JSInvokable, _IsolateEncodable {
|
||||
|
||||
/// Dart function wrapper for isolate
|
||||
class IsolateFunction extends JSInvokable implements _IsolateEncodable {
|
||||
int _isolateId;
|
||||
SendPort _port;
|
||||
JSInvokable _invokable;
|
||||
int? _isolateId;
|
||||
SendPort? _port;
|
||||
JSInvokable? _invokable;
|
||||
IsolateFunction._fromId(this._isolateId, this._port);
|
||||
|
||||
IsolateFunction._new(this._invokable) {
|
||||
@@ -195,18 +202,17 @@ class IsolateFunction extends JSInvokable implements _IsolateEncodable {
|
||||
}
|
||||
IsolateFunction(Function func) : this._new(_DartFunction(func));
|
||||
|
||||
static ReceivePort _invokeHandler;
|
||||
static ReceivePort? _invokeHandler;
|
||||
static Set<IsolateFunction> _handlers = Set();
|
||||
|
||||
static get _handlePort {
|
||||
if (_invokeHandler == null) {
|
||||
_invokeHandler = ReceivePort();
|
||||
_invokeHandler.listen((msg) async {
|
||||
_invokeHandler!.listen((msg) async {
|
||||
final msgPort = msg[#port];
|
||||
try {
|
||||
final handler = _handlers.firstWhere(
|
||||
final handler = _handlers.firstWhereOrNull(
|
||||
(v) => identityHashCode(v) == msg[#handler],
|
||||
orElse: () => null,
|
||||
);
|
||||
if (handler == null) throw JSError('handler released');
|
||||
final ret = _encodeData(await handler._handle(msg[#msg]));
|
||||
@@ -220,13 +226,14 @@ class IsolateFunction extends JSInvokable implements _IsolateEncodable {
|
||||
}
|
||||
});
|
||||
}
|
||||
return _invokeHandler.sendPort;
|
||||
return _invokeHandler!.sendPort;
|
||||
}
|
||||
|
||||
_send(msg) async {
|
||||
if (_port == null) return _handle(msg);
|
||||
final port = _port;
|
||||
if (port == null) return _handle(msg);
|
||||
final evaluatePort = ReceivePort();
|
||||
_port.send({
|
||||
port.send({
|
||||
#handler: _isolateId,
|
||||
#msg: msg,
|
||||
#port: evaluatePort.sendPort,
|
||||
@@ -240,6 +247,7 @@ class IsolateFunction extends JSInvokable implements _IsolateEncodable {
|
||||
_destroy() {
|
||||
_handlers.remove(this);
|
||||
_invokable?.free();
|
||||
_invokable = null;
|
||||
}
|
||||
|
||||
_handle(msg) async {
|
||||
@@ -257,7 +265,7 @@ class IsolateFunction extends JSInvokable implements _IsolateEncodable {
|
||||
}
|
||||
final List args = _decodeData(msg[#args]);
|
||||
final thisVal = _decodeData(msg[#thisVal]);
|
||||
return _invokable.invoke(args, thisVal);
|
||||
return _invokable?.invoke(args, thisVal);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -270,7 +278,7 @@ class IsolateFunction extends JSInvokable implements _IsolateEncodable {
|
||||
});
|
||||
}
|
||||
|
||||
static IsolateFunction _decode(Map obj) {
|
||||
static IsolateFunction? _decode(Map obj) {
|
||||
if (obj.containsKey(#jsFunctionPort))
|
||||
return IsolateFunction._fromId(
|
||||
obj[#jsFunctionId],
|
||||
|
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
part of '../flutter_qjs.dart';
|
||||
|
||||
dynamic _parseJSException(Pointer<JSContext> ctx, [Pointer<JSValue> perr]) {
|
||||
dynamic _parseJSException(Pointer<JSContext> ctx, [Pointer<JSValue>? perr]) {
|
||||
final e = perr ?? jsGetException(ctx);
|
||||
var err;
|
||||
try {
|
||||
@@ -24,7 +24,7 @@ void _definePropertyValue(
|
||||
Pointer<JSValue> obj,
|
||||
dynamic key,
|
||||
dynamic val, {
|
||||
Map<dynamic, Pointer<JSValue>> cache,
|
||||
Map<dynamic, Pointer<JSValue>>? cache,
|
||||
}) {
|
||||
final jsAtomVal = _dartToJs(ctx, key, cache: cache);
|
||||
final jsAtom = jsValueToAtom(ctx, jsAtomVal);
|
||||
@@ -43,7 +43,7 @@ Pointer<JSValue> _jsGetPropertyValue(
|
||||
Pointer<JSContext> ctx,
|
||||
Pointer<JSValue> obj,
|
||||
dynamic key, {
|
||||
Map<dynamic, dynamic> cache,
|
||||
Map<dynamic, Pointer<JSValue>>? cache,
|
||||
}) {
|
||||
final jsAtomVal = _dartToJs(ctx, key, cache: cache);
|
||||
final jsAtom = jsValueToAtom(ctx, jsAtomVal);
|
||||
@@ -54,7 +54,7 @@ Pointer<JSValue> _jsGetPropertyValue(
|
||||
}
|
||||
|
||||
Pointer<JSValue> _dartToJs(Pointer<JSContext> ctx, dynamic val,
|
||||
{Map<dynamic, Pointer<JSValue>> cache}) {
|
||||
{Map<dynamic, Pointer<JSValue>>? cache}) {
|
||||
if (val == null) return jsUNDEFINED();
|
||||
if (val is Error) return _dartToJs(ctx, JSError(val, val.stackTrace));
|
||||
if (val is Exception) return _dartToJs(ctx, JSError(val));
|
||||
@@ -65,7 +65,7 @@ Pointer<JSValue> _dartToJs(Pointer<JSContext> ctx, dynamic val,
|
||||
_definePropertyValue(ctx, ret, "stack", val.stack);
|
||||
return ret;
|
||||
}
|
||||
if (val is _JSObject) return jsDupValue(ctx, val._val);
|
||||
if (val is _JSObject) return jsDupValue(ctx, val._val!);
|
||||
if (val is Future) {
|
||||
final resolvingFunc = malloc<Uint8>(sizeOfJSValue * 2).cast<JSValue>();
|
||||
final resolvingFunc2 =
|
||||
@@ -104,7 +104,7 @@ Pointer<JSValue> _dartToJs(Pointer<JSContext> ctx, dynamic val,
|
||||
return ret;
|
||||
}
|
||||
if (cache.containsKey(val)) {
|
||||
return jsDupValue(ctx, cache[val]);
|
||||
return jsDupValue(ctx, cache[val]!);
|
||||
}
|
||||
if (val is List) {
|
||||
final ret = jsNewArray(ctx);
|
||||
@@ -141,7 +141,7 @@ Pointer<JSValue> _dartToJs(Pointer<JSContext> ctx, dynamic val,
|
||||
}
|
||||
|
||||
dynamic _jsToDart(Pointer<JSContext> ctx, Pointer<JSValue> val,
|
||||
{Map<int, dynamic> cache}) {
|
||||
{Map<int, dynamic>? cache}) {
|
||||
if (cache == null) cache = Map();
|
||||
final tag = jsValueGetTag(val);
|
||||
if (jsTagIsFloat64(tag) != 0) {
|
||||
@@ -156,8 +156,8 @@ dynamic _jsToDart(Pointer<JSContext> ctx, Pointer<JSValue> val,
|
||||
return jsToCString(ctx, val);
|
||||
case JSTag.OBJECT:
|
||||
final rt = jsGetRuntime(ctx);
|
||||
final dartObjectClassId = runtimeOpaques[rt].dartObjectClassId;
|
||||
if (dartObjectClassId != 0) {
|
||||
final dartObjectClassId = runtimeOpaques[rt]?.dartObjectClassId;
|
||||
if (dartObjectClassId != null) {
|
||||
final dartObject = _DartObject.fromAddress(
|
||||
rt, jsGetObjectOpaque(val, dartObjectClassId));
|
||||
if (dartObject != null) return dartObject._obj;
|
||||
@@ -243,7 +243,6 @@ dynamic _jsToDart(Pointer<JSContext> ctx, Pointer<JSValue> val,
|
||||
malloc.free(ptab);
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return null;
|
||||
|
Reference in New Issue
Block a user