mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 21:37:24 +00:00
v0.3.1
This commit is contained in:
217
lib/isolate.dart
217
lib/isolate.dart
@@ -11,205 +11,8 @@ import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:flutter_qjs/flutter_qjs.dart';
|
||||
import 'package:flutter_qjs/wrapper.dart';
|
||||
|
||||
class IsolateJSFunction implements QjsInvokable {
|
||||
int val;
|
||||
int ctx;
|
||||
SendPort port;
|
||||
IsolateJSFunction(this.ctx, this.val, this.port);
|
||||
|
||||
Future invoke(List arguments, [thisVal]) async {
|
||||
if (0 == val ?? 0) return;
|
||||
var evaluatePort = ReceivePort();
|
||||
port.send({
|
||||
'type': 'call',
|
||||
'ctx': ctx,
|
||||
'val': val,
|
||||
'args': _encodeData(arguments),
|
||||
'this': _encodeData(thisVal),
|
||||
'port': evaluatePort.sendPort,
|
||||
});
|
||||
var result = await evaluatePort.first;
|
||||
evaluatePort.close();
|
||||
if (result['data'] != null)
|
||||
return _decodeData(result['data'], port);
|
||||
else
|
||||
throw result['error'];
|
||||
}
|
||||
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) {
|
||||
return invoke(
|
||||
invocation.positionalArguments,
|
||||
invocation.namedArguments[#thisVal],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class IsolateFunction implements QjsInvokable {
|
||||
SendPort _port;
|
||||
SendPort func;
|
||||
IsolateFunction(this.func, this._port);
|
||||
|
||||
static IsolateFunction bind(Function func, SendPort port) {
|
||||
final funcPort = ReceivePort();
|
||||
funcPort.listen((msg) async {
|
||||
var data;
|
||||
SendPort msgPort = msg['port'];
|
||||
try {
|
||||
List args = _decodeData(msg['args'], port);
|
||||
Map thisVal = _decodeData(msg['this'], port);
|
||||
data = await FlutterQjs.applyFunction(func, args, thisVal);
|
||||
if (msgPort != null)
|
||||
msgPort.send({
|
||||
'data': _encodeData(data),
|
||||
});
|
||||
} catch (e, stack) {
|
||||
if (msgPort != null)
|
||||
msgPort.send({
|
||||
'error': e.toString() + "\n" + stack.toString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
return IsolateFunction(funcPort.sendPort, port);
|
||||
}
|
||||
|
||||
Future invoke(List positionalArguments, [thisVal]) async {
|
||||
if (func == null) return;
|
||||
var evaluatePort = ReceivePort();
|
||||
func.send({
|
||||
'args': _encodeData(positionalArguments),
|
||||
'this': _encodeData(thisVal),
|
||||
'port': evaluatePort.sendPort,
|
||||
});
|
||||
var result = await evaluatePort.first;
|
||||
evaluatePort.close();
|
||||
if (result['data'] != null)
|
||||
return _decodeData(result['data'], _port);
|
||||
else
|
||||
throw result['error'];
|
||||
}
|
||||
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) {
|
||||
return invoke(
|
||||
invocation.positionalArguments,
|
||||
invocation.namedArguments[#thisVal],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dynamic _encodeData(dynamic data, {Map<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;
|
||||
}
|
||||
import 'flutter_qjs.dart';
|
||||
import 'wrapper.dart';
|
||||
|
||||
void _runJsIsolate(Map spawnMessage) async {
|
||||
SendPort sendPort = spawnMessage['port'];
|
||||
@@ -256,11 +59,11 @@ void _runJsIsolate(Map spawnMessage) async {
|
||||
break;
|
||||
case 'call':
|
||||
data = JSFunction.fromAddress(
|
||||
msg['ctx'],
|
||||
msg['val'],
|
||||
Pointer.fromAddress(msg['ctx']),
|
||||
Pointer.fromAddress(msg['val']),
|
||||
).invoke(
|
||||
_decodeData(msg['args'], null),
|
||||
_decodeData(msg['this'], null),
|
||||
decodeData(msg['args'], null),
|
||||
decodeData(msg['this'], null),
|
||||
);
|
||||
break;
|
||||
case 'close':
|
||||
@@ -271,7 +74,7 @@ void _runJsIsolate(Map spawnMessage) async {
|
||||
}
|
||||
if (msgPort != null)
|
||||
msgPort.send({
|
||||
'data': _encodeData(data),
|
||||
'data': encodeData(data),
|
||||
});
|
||||
} catch (e, stack) {
|
||||
if (msgPort != null)
|
||||
@@ -389,10 +192,10 @@ class IsolateQjs {
|
||||
'flag': evalFlags,
|
||||
'port': evaluatePort.sendPort,
|
||||
});
|
||||
var result = await evaluatePort.first;
|
||||
Map result = await evaluatePort.first;
|
||||
evaluatePort.close();
|
||||
if (result['error'] == null) {
|
||||
return _decodeData(result['data'], sendPort);
|
||||
if (result.containsKey('data')) {
|
||||
return decodeData(result['data'], sendPort);
|
||||
} else
|
||||
throw result['error'];
|
||||
}
|
||||
|
Reference in New Issue
Block a user