nullsafety

This commit is contained in:
ekibun
2021-02-22 16:15:46 +08:00
parent 8fb26b20af
commit e87f50956a
9 changed files with 176 additions and 140 deletions

View File

@@ -6,6 +6,15 @@
* @LastEditTime: 2020-12-02 11:36:40
-->
## 0.3.6
* upgrade ffi to 1.0.0.
* nullsafety.
## 0.3.5
* downgrade ffi to 0.1.3.
## 0.3.4
* upgrade ffi to 1.0.0.

View File

@@ -75,7 +75,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.3.4"
version: "0.3.6"
flutter_test:
dependency: "direct dev"
description: flutter

View File

@@ -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;
}
}

View File

@@ -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,22 +232,23 @@ void jsFreeRuntime(
Pointer<JSRuntime> rt,
) {
final referenceleak = <String>[];
final opaque = runtimeOpaques[rt];
if (opaque != null) {
while (true) {
final ref = runtimeOpaques[rt]
?._ref
?.firstWhere((ref) => ref is JSRefLeakable, orElse: () => null);
final ref = opaque._ref.firstWhereOrNull((ref) => ref is JSRefLeakable);
if (ref == null) break;
ref.destroy();
runtimeOpaques[rt]?._ref?.remove(ref);
runtimeOpaques[rt]?._ref.remove(ref);
}
while (0 < runtimeOpaques[rt]?._ref?.length ?? 0) {
final ref = runtimeOpaques[rt]?._ref?.first;
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) {
throw ('reference leak:\n ADDR\tREF\tTYPE\tPROP\n' +
@@ -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;
}

View File

@@ -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,

View File

@@ -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],

View File

@@ -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;

View File

@@ -1,11 +1,11 @@
name: flutter_qjs
description: This plugin is a simple js engine for flutter using the `quickjs` project. Plugin currently supports all the platforms except web!
version: 0.3.4
version: 0.3.6
homepage: https://github.com/ekibun/flutter_qjs
environment:
sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.20.0 <2.0.0"
sdk: ">=2.12.0-0 <3.0.0"
flutter: ">=1.20.0"
dependencies:
flutter:

View File

@@ -135,7 +135,7 @@ void main() async {
});
test('isolate bind this', () async {
final qjs = IsolateQjs();
JSInvokable localVar;
JSInvokable? localVar;
JSInvokable setToGlobal = await qjs
.evaluate('(name, func)=>{ this[name] = func }', name: '<eval>');
final func = IsolateFunction((args) {
@@ -146,8 +146,8 @@ void main() async {
func.free();
setToGlobal.free();
final testFuncRet = await qjs.evaluate('test(()=>"ret")', name: '<eval>');
expect(await localVar.invoke([]), 'ret', reason: 'bind function');
localVar.free();
expect(await localVar?.invoke([]), 'ret', reason: 'bind function');
localVar?.free();
expect(testFuncRet, 'ret', reason: 'bind function args return');
await qjs.close();
});