mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 05:27:23 +00:00
fix qjs memory leak
This commit is contained in:
15
lib/ffi.dart
15
lib/ffi.dart
@@ -3,7 +3,7 @@
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-09-19 10:29:04
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-09-27 01:12:16
|
||||
* @LastEditTime: 2020-10-03 23:27:15
|
||||
*/
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
@@ -744,3 +744,16 @@ final Pointer Function(
|
||||
Pointer,
|
||||
)>>("jsNewPromiseCapability")
|
||||
.asFunction();
|
||||
|
||||
/// void jsFree(JSContext *ctx, void *ptab)
|
||||
final void Function(
|
||||
Pointer ctx,
|
||||
Pointer ptab,
|
||||
) jsFree = qjsLib
|
||||
.lookup<
|
||||
NativeFunction<
|
||||
Void Function(
|
||||
Pointer,
|
||||
Pointer,
|
||||
)>>("jsFree")
|
||||
.asFunction();
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-08-08 08:29:09
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-10-03 00:18:49
|
||||
* @LastEditTime: 2020-10-03 21:55:07
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
@@ -116,9 +116,10 @@ class FlutterQjs {
|
||||
}
|
||||
|
||||
/// Evaluate js script.
|
||||
Future<dynamic> evaluate(String command, String name) async {
|
||||
Future<dynamic> evaluate(String command, {String name, int evalFlags}) async {
|
||||
_ensureEngine();
|
||||
var jsval = jsEval(_ctx, command, name, JSEvalType.GLOBAL);
|
||||
var jsval =
|
||||
jsEval(_ctx, command, name ?? "<eval>", evalFlags ?? JSEvalType.GLOBAL);
|
||||
if (jsIsException(jsval) != 0) {
|
||||
throw Exception(parseJSException(_ctx));
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-10-02 13:49:03
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-10-03 00:18:40
|
||||
* @LastEditTime: 2020-10-03 22:21:31
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
@@ -123,18 +123,21 @@ void _runJsIsolate(Map spawnMessage) async {
|
||||
sendPort.send(port.sendPort);
|
||||
qjs.setMethodHandler(methodHandler);
|
||||
qjs.setModuleHandler((name) {
|
||||
var ptr = allocate<Int64>();
|
||||
var ptr = allocate<Pointer<Utf8>>();
|
||||
ptr.value = Pointer.fromAddress(0);
|
||||
sendPort.send({
|
||||
'type': 'module',
|
||||
'name': name,
|
||||
'ptr': ptr.address,
|
||||
});
|
||||
ptr.value = 0;
|
||||
while (ptr.value == 0) sleep(Duration.zero);
|
||||
print(ptr.value);
|
||||
if (ptr.value == -1) throw Exception("Module Not found");
|
||||
var strptr = Pointer<Utf8>.fromAddress(ptr.value);
|
||||
var ret = Utf8.fromUtf8(strptr);
|
||||
while (ptr.value.address == 0) sleep(Duration.zero);
|
||||
if (ptr.value.address == -1) throw Exception("Module Not found");
|
||||
var ret = Utf8.fromUtf8(ptr.value);
|
||||
sendPort.send({
|
||||
'type': 'release',
|
||||
'ptr': ptr.value.address,
|
||||
});
|
||||
free(ptr);
|
||||
return ret;
|
||||
});
|
||||
qjs.dispatch();
|
||||
@@ -144,7 +147,11 @@ void _runJsIsolate(Map spawnMessage) async {
|
||||
try {
|
||||
switch (msg['type']) {
|
||||
case 'evaluate':
|
||||
data = await qjs.evaluate(msg['command'], msg['name']);
|
||||
data = await qjs.evaluate(
|
||||
msg['command'],
|
||||
name: msg['name'],
|
||||
evalFlags: msg['flag'],
|
||||
);
|
||||
break;
|
||||
case 'call':
|
||||
data = JSFunction.fromAddress(
|
||||
@@ -174,7 +181,7 @@ typedef JsAsyncModuleHandler = Future<String> Function(String name);
|
||||
typedef JsIsolateSpawn = void Function(SendPort sendPort);
|
||||
|
||||
class IsolateQjs {
|
||||
SendPort _sendPort;
|
||||
Future<SendPort> _sendPort;
|
||||
JsMethodHandler _methodHandler;
|
||||
JsAsyncModuleHandler _moduleHandler;
|
||||
|
||||
@@ -182,7 +189,7 @@ class IsolateQjs {
|
||||
/// The function must be a top-level function or a static method
|
||||
IsolateQjs(this._methodHandler);
|
||||
|
||||
Future<void> _ensureEngine() async {
|
||||
_ensureEngine() {
|
||||
if (_sendPort != null) return;
|
||||
ReceivePort port = ReceivePort();
|
||||
Isolate.spawn(
|
||||
@@ -193,28 +200,30 @@ class IsolateQjs {
|
||||
},
|
||||
errorsAreFatal: true,
|
||||
);
|
||||
var completer = Completer();
|
||||
var completer = Completer<SendPort>();
|
||||
port.listen((msg) async {
|
||||
if (msg is SendPort && !completer.isCompleted) {
|
||||
_sendPort = msg;
|
||||
completer.complete();
|
||||
completer.complete(msg);
|
||||
return;
|
||||
}
|
||||
switch (msg['type']) {
|
||||
case 'module':
|
||||
var ptr = Pointer<Int64>.fromAddress(msg['ptr']);
|
||||
var ptr = Pointer<Pointer>.fromAddress(msg['ptr']);
|
||||
try {
|
||||
ptr.value = Utf8.toUtf8(await _moduleHandler(msg['name'])).address;
|
||||
ptr.value = Utf8.toUtf8(await _moduleHandler(msg['name']));
|
||||
} catch (e) {
|
||||
ptr.value = -1;
|
||||
ptr.value = Pointer.fromAddress(-1);
|
||||
}
|
||||
break;
|
||||
case 'release':
|
||||
free(Pointer.fromAddress(msg['ptr']));
|
||||
break;
|
||||
}
|
||||
}, onDone: () {
|
||||
close();
|
||||
if (!completer.isCompleted) completer.completeError('isolate close');
|
||||
});
|
||||
await completer.future;
|
||||
_sendPort = completer.future;
|
||||
}
|
||||
|
||||
/// Set a handler to manage js module.
|
||||
@@ -223,24 +232,29 @@ class IsolateQjs {
|
||||
}
|
||||
|
||||
close() {
|
||||
_sendPort.send({
|
||||
'type': 'close',
|
||||
if (_sendPort == null) return;
|
||||
_sendPort.then((sendPort) {
|
||||
sendPort.send({
|
||||
'type': 'close',
|
||||
});
|
||||
});
|
||||
_sendPort = null;
|
||||
}
|
||||
|
||||
Future<dynamic> evaluate(String command, String name) async {
|
||||
await _ensureEngine();
|
||||
Future<dynamic> evaluate(String command, {String name, int evalFlags}) async {
|
||||
_ensureEngine();
|
||||
var evaluatePort = ReceivePort();
|
||||
_sendPort.send({
|
||||
var sendPort = await _sendPort;
|
||||
sendPort.send({
|
||||
'type': 'evaluate',
|
||||
'command': command,
|
||||
'name': name,
|
||||
'flag': evalFlags,
|
||||
'port': evaluatePort.sendPort,
|
||||
});
|
||||
var result = await evaluatePort.first;
|
||||
if (result['data'] != null)
|
||||
return _decodeData(result['data'], _sendPort);
|
||||
return _decodeData(result['data'], sendPort);
|
||||
else
|
||||
throw result['error'];
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
* @Author: ekibun
|
||||
* @Date: 2020-09-19 22:07:47
|
||||
* @LastEditors: ekibun
|
||||
* @LastEditTime: 2020-10-02 16:37:16
|
||||
* @LastEditTime: 2020-10-03 23:27:36
|
||||
*/
|
||||
import 'dart:async';
|
||||
import 'dart:ffi';
|
||||
@@ -84,11 +84,12 @@ class JSFunction extends JSRefValue {
|
||||
jsFreeValue(ctx, jsArg);
|
||||
}
|
||||
bool isException = jsIsException(jsRet) != 0;
|
||||
var ret = jsToDart(ctx, jsRet);
|
||||
jsFreeValue(ctx, jsRet);
|
||||
if (isException) {
|
||||
jsFreeValue(ctx, jsRet);
|
||||
throw Exception(parseJSException(ctx));
|
||||
}
|
||||
var ret = jsToDart(ctx, jsRet);
|
||||
jsFreeValue(ctx, jsRet);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -257,6 +258,7 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
||||
jsFreeValue(ctx, jsProp);
|
||||
jsFreeAtom(ctx, jsAtom);
|
||||
}
|
||||
jsFree(ctx, ptab.value);
|
||||
free(ptab);
|
||||
return ret;
|
||||
}
|
||||
|
Reference in New Issue
Block a user