fix qjs memory leak

This commit is contained in:
ekibun
2020-10-03 23:36:39 +08:00
parent 8a72bac6a9
commit d294dd59bc
13 changed files with 125 additions and 84 deletions

View File

@@ -3,8 +3,12 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-08-08 08:16:50 * @Date: 2020-08-08 08:16:50
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-10-03 00:28:18 * @LastEditTime: 2020-10-03 23:34:30
--> -->
## 0.1.2
* fix qjs memory leak.
## 0.1.1 ## 0.1.1
* run on isolate. * run on isolate.

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-08-08 08:16:50 * @Date: 2020-08-08 08:16:50
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-10-03 00:36:36 * @LastEditTime: 2020-10-03 00:44:41
--> -->
# flutter_qjs # flutter_qjs
@@ -67,7 +67,7 @@ channel("http", ["http://example.com/"]);
~~I cannot find a way to convert the sync ffi callback into an async function. So the assets files received by async function `rootBundle.loadString` cannot be used in this version. I will appreciate it if you can provide me a solution to make `ModuleHandler` async.~~ ~~I cannot find a way to convert the sync ffi callback into an async function. So the assets files received by async function `rootBundle.loadString` cannot be used in this version. I will appreciate it if you can provide me a solution to make `ModuleHandler` async.~~
To use async function in module handler, try [Run on isolate thread](#isolate) To use async function in module handler, try [Run on isolate thread](#Run-on-isolate-thread)
```dart ```dart
await engine.setModuleHandler((String module) { await engine.setModuleHandler((String module) {
@@ -94,7 +94,7 @@ try {
5. Method `recreate` can destroy quickjs runtime that can be recreated again if you call `evaluate`, `recreat` can be used to reset the module cache. Call `close` to stop `dispatch` when you do not need it. 5. Method `recreate` can destroy quickjs runtime that can be recreated again if you call `evaluate`, `recreat` can be used to reset the module cache. Call `close` to stop `dispatch` when you do not need it.
### <span id="isolate">Run on isolate thread</span> ### Run on isolate thread
1. Create a `IsolateQjs` object, pass a handler to implement js-dart interaction. The handler is used in isolate, so the function must be a top-level function or a static method. 1. Create a `IsolateQjs` object, pass a handler to implement js-dart interaction. The handler is used in isolate, so the function must be a top-level function or a static method.

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-09-06 18:32:45 * @Date: 2020-09-06 18:32:45
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-09-24 22:32:15 * @LastEditTime: 2020-10-03 23:26:14
*/ */
#include "ffi.h" #include "ffi.h"
#include <functional> #include <functional>
@@ -293,4 +293,9 @@ extern "C"
{ {
return new JSValue(JS_NewPromiseCapability(ctx, resolving_funcs)); return new JSValue(JS_NewPromiseCapability(ctx, resolving_funcs));
} }
DLLEXPORT void jsFree(JSContext *ctx, void *ptab)
{
js_free(ctx, ptab);
}
} }

View File

@@ -106,4 +106,6 @@ extern "C"
DLLEXPORT int32_t jsExecutePendingJob(JSRuntime *rt); DLLEXPORT int32_t jsExecutePendingJob(JSRuntime *rt);
DLLEXPORT JSValue *jsNewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); DLLEXPORT JSValue *jsNewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
DLLEXPORT void jsFree(JSContext *ctx, void *ptab);
} }

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-08-08 08:16:51 * @Date: 2020-08-08 08:16:51
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-10-03 00:38:41 * @LastEditTime: 2020-10-03 21:37:22
*/ */
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:typed_data'; import 'dart:typed_data';
@@ -103,8 +103,8 @@ class _TestPageState extends State<TestPage> {
onPressed: () async { onPressed: () async {
_ensureEngine(); _ensureEngine();
try { try {
resp = (await engine.evaluate( resp = (await engine.evaluate(_controller.text ?? '',
_controller.text ?? '', "<eval>")) name: "<eval>"))
.toString(); .toString();
} catch (e) { } catch (e) {
resp = e.toString(); resp = e.toString();

View File

@@ -7,42 +7,42 @@ packages:
name: async name: async
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.5.0-nullsafety" version: "2.5.0-nullsafety.1"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.0-nullsafety" version: "2.1.0-nullsafety.1"
characters: characters:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety.2" version: "1.1.0-nullsafety.3"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
name: charcode name: charcode
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.2.0-nullsafety" version: "1.2.0-nullsafety.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety" version: "1.1.0-nullsafety.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.15.0-nullsafety.2" version: "1.15.0-nullsafety.3"
dio: dio:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -56,7 +56,7 @@ packages:
name: fake_async name: fake_async
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety" version: "1.2.0-nullsafety.1"
ffi: ffi:
dependency: transitive dependency: transitive
description: description:
@@ -82,7 +82,7 @@ packages:
path: ".." path: ".."
relative: true relative: true
source: path source: path
version: "0.1.1" version: "0.1.2"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@@ -108,21 +108,21 @@ packages:
name: matcher name: matcher
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.12.10-nullsafety" version: "0.12.10-nullsafety.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0-nullsafety.2" version: "1.3.0-nullsafety.3"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.8.0-nullsafety" version: "1.8.0-nullsafety.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@@ -134,56 +134,56 @@ packages:
name: source_span name: source_span
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.8.0-nullsafety" version: "1.8.0-nullsafety.2"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.10.0-nullsafety" version: "1.10.0-nullsafety.2"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.0-nullsafety" version: "2.1.0-nullsafety.1"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety" version: "1.1.0-nullsafety.1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.2.0-nullsafety" version: "1.2.0-nullsafety.1"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.19-nullsafety" version: "0.2.19-nullsafety.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0-nullsafety.2" version: "1.3.0-nullsafety.3"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.0-nullsafety.2" version: "2.1.0-nullsafety.3"
sdks: sdks:
dart: ">=2.10.0-0.0.dev <2.10.0" dart: ">=2.10.0-110 <=2.11.0-181.0.dev"
flutter: ">=1.20.0 <2.0.0" flutter: ">=1.20.0 <2.0.0"

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-09-19 10:29:04 * @Date: 2020-09-19 10:29:04
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-09-27 01:12:16 * @LastEditTime: 2020-10-03 23:27:15
*/ */
import 'dart:ffi'; import 'dart:ffi';
import 'dart:io'; import 'dart:io';
@@ -744,3 +744,16 @@ final Pointer Function(
Pointer, Pointer,
)>>("jsNewPromiseCapability") )>>("jsNewPromiseCapability")
.asFunction(); .asFunction();
/// void jsFree(JSContext *ctx, void *ptab)
final void Function(
Pointer ctx,
Pointer ptab,
) jsFree = qjsLib
.lookup<
NativeFunction<
Void Function(
Pointer,
Pointer,
)>>("jsFree")
.asFunction();

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-08-08 08:29:09 * @Date: 2020-08-08 08:29:09
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-10-03 00:18:49 * @LastEditTime: 2020-10-03 21:55:07
*/ */
import 'dart:async'; import 'dart:async';
import 'dart:ffi'; import 'dart:ffi';
@@ -116,9 +116,10 @@ class FlutterQjs {
} }
/// Evaluate js script. /// Evaluate js script.
Future<dynamic> evaluate(String command, String name) async { Future<dynamic> evaluate(String command, {String name, int evalFlags}) async {
_ensureEngine(); _ensureEngine();
var jsval = jsEval(_ctx, command, name, JSEvalType.GLOBAL); var jsval =
jsEval(_ctx, command, name ?? "<eval>", evalFlags ?? JSEvalType.GLOBAL);
if (jsIsException(jsval) != 0) { if (jsIsException(jsval) != 0) {
throw Exception(parseJSException(_ctx)); throw Exception(parseJSException(_ctx));
} }

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-10-02 13:49:03 * @Date: 2020-10-02 13:49:03
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-10-03 00:18:40 * @LastEditTime: 2020-10-03 22:21:31
*/ */
import 'dart:async'; import 'dart:async';
import 'dart:ffi'; import 'dart:ffi';
@@ -123,18 +123,21 @@ void _runJsIsolate(Map spawnMessage) async {
sendPort.send(port.sendPort); sendPort.send(port.sendPort);
qjs.setMethodHandler(methodHandler); qjs.setMethodHandler(methodHandler);
qjs.setModuleHandler((name) { qjs.setModuleHandler((name) {
var ptr = allocate<Int64>(); var ptr = allocate<Pointer<Utf8>>();
ptr.value = Pointer.fromAddress(0);
sendPort.send({ sendPort.send({
'type': 'module', 'type': 'module',
'name': name, 'name': name,
'ptr': ptr.address, 'ptr': ptr.address,
}); });
ptr.value = 0; while (ptr.value.address == 0) sleep(Duration.zero);
while (ptr.value == 0) sleep(Duration.zero); if (ptr.value.address == -1) throw Exception("Module Not found");
print(ptr.value); var ret = Utf8.fromUtf8(ptr.value);
if (ptr.value == -1) throw Exception("Module Not found"); sendPort.send({
var strptr = Pointer<Utf8>.fromAddress(ptr.value); 'type': 'release',
var ret = Utf8.fromUtf8(strptr); 'ptr': ptr.value.address,
});
free(ptr);
return ret; return ret;
}); });
qjs.dispatch(); qjs.dispatch();
@@ -144,7 +147,11 @@ void _runJsIsolate(Map spawnMessage) async {
try { try {
switch (msg['type']) { switch (msg['type']) {
case 'evaluate': case 'evaluate':
data = await qjs.evaluate(msg['command'], msg['name']); data = await qjs.evaluate(
msg['command'],
name: msg['name'],
evalFlags: msg['flag'],
);
break; break;
case 'call': case 'call':
data = JSFunction.fromAddress( data = JSFunction.fromAddress(
@@ -174,7 +181,7 @@ typedef JsAsyncModuleHandler = Future<String> Function(String name);
typedef JsIsolateSpawn = void Function(SendPort sendPort); typedef JsIsolateSpawn = void Function(SendPort sendPort);
class IsolateQjs { class IsolateQjs {
SendPort _sendPort; Future<SendPort> _sendPort;
JsMethodHandler _methodHandler; JsMethodHandler _methodHandler;
JsAsyncModuleHandler _moduleHandler; JsAsyncModuleHandler _moduleHandler;
@@ -182,7 +189,7 @@ class IsolateQjs {
/// The function must be a top-level function or a static method /// The function must be a top-level function or a static method
IsolateQjs(this._methodHandler); IsolateQjs(this._methodHandler);
Future<void> _ensureEngine() async { _ensureEngine() {
if (_sendPort != null) return; if (_sendPort != null) return;
ReceivePort port = ReceivePort(); ReceivePort port = ReceivePort();
Isolate.spawn( Isolate.spawn(
@@ -193,28 +200,30 @@ class IsolateQjs {
}, },
errorsAreFatal: true, errorsAreFatal: true,
); );
var completer = Completer(); var completer = Completer<SendPort>();
port.listen((msg) async { port.listen((msg) async {
if (msg is SendPort && !completer.isCompleted) { if (msg is SendPort && !completer.isCompleted) {
_sendPort = msg; completer.complete(msg);
completer.complete();
return; return;
} }
switch (msg['type']) { switch (msg['type']) {
case 'module': case 'module':
var ptr = Pointer<Int64>.fromAddress(msg['ptr']); var ptr = Pointer<Pointer>.fromAddress(msg['ptr']);
try { try {
ptr.value = Utf8.toUtf8(await _moduleHandler(msg['name'])).address; ptr.value = Utf8.toUtf8(await _moduleHandler(msg['name']));
} catch (e) { } catch (e) {
ptr.value = -1; ptr.value = Pointer.fromAddress(-1);
} }
break; break;
case 'release':
free(Pointer.fromAddress(msg['ptr']));
break;
} }
}, onDone: () { }, onDone: () {
close(); close();
if (!completer.isCompleted) completer.completeError('isolate close'); if (!completer.isCompleted) completer.completeError('isolate close');
}); });
await completer.future; _sendPort = completer.future;
} }
/// Set a handler to manage js module. /// Set a handler to manage js module.
@@ -223,24 +232,29 @@ class IsolateQjs {
} }
close() { close() {
_sendPort.send({ if (_sendPort == null) return;
_sendPort.then((sendPort) {
sendPort.send({
'type': 'close', 'type': 'close',
}); });
});
_sendPort = null; _sendPort = null;
} }
Future<dynamic> evaluate(String command, String name) async { Future<dynamic> evaluate(String command, {String name, int evalFlags}) async {
await _ensureEngine(); _ensureEngine();
var evaluatePort = ReceivePort(); var evaluatePort = ReceivePort();
_sendPort.send({ var sendPort = await _sendPort;
sendPort.send({
'type': 'evaluate', 'type': 'evaluate',
'command': command, 'command': command,
'name': name, 'name': name,
'flag': evalFlags,
'port': evaluatePort.sendPort, 'port': evaluatePort.sendPort,
}); });
var result = await evaluatePort.first; var result = await evaluatePort.first;
if (result['data'] != null) if (result['data'] != null)
return _decodeData(result['data'], _sendPort); return _decodeData(result['data'], sendPort);
else else
throw result['error']; throw result['error'];
} }

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-09-19 22:07:47 * @Date: 2020-09-19 22:07:47
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-10-02 16:37:16 * @LastEditTime: 2020-10-03 23:27:36
*/ */
import 'dart:async'; import 'dart:async';
import 'dart:ffi'; import 'dart:ffi';
@@ -84,11 +84,12 @@ class JSFunction extends JSRefValue {
jsFreeValue(ctx, jsArg); jsFreeValue(ctx, jsArg);
} }
bool isException = jsIsException(jsRet) != 0; bool isException = jsIsException(jsRet) != 0;
var ret = jsToDart(ctx, jsRet);
jsFreeValue(ctx, jsRet);
if (isException) { if (isException) {
jsFreeValue(ctx, jsRet);
throw Exception(parseJSException(ctx)); throw Exception(parseJSException(ctx));
} }
var ret = jsToDart(ctx, jsRet);
jsFreeValue(ctx, jsRet);
return ret; return ret;
} }
@@ -257,6 +258,7 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
jsFreeValue(ctx, jsProp); jsFreeValue(ctx, jsProp);
jsFreeAtom(ctx, jsAtom); jsFreeAtom(ctx, jsAtom);
} }
jsFree(ctx, ptab.value);
free(ptab); free(ptab);
return ret; return ret;
} }

View File

@@ -7,49 +7,49 @@ packages:
name: async name: async
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.5.0-nullsafety" version: "2.5.0-nullsafety.1"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
name: boolean_selector name: boolean_selector
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.0-nullsafety" version: "2.1.0-nullsafety.1"
characters: characters:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety.2" version: "1.1.0-nullsafety.3"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
name: charcode name: charcode
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.2.0-nullsafety" version: "1.2.0-nullsafety.1"
clock: clock:
dependency: transitive dependency: transitive
description: description:
name: clock name: clock
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety" version: "1.1.0-nullsafety.1"
collection: collection:
dependency: transitive dependency: transitive
description: description:
name: collection name: collection
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.15.0-nullsafety.2" version: "1.15.0-nullsafety.3"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
name: fake_async name: fake_async
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety" version: "1.2.0-nullsafety.1"
ffi: ffi:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -73,21 +73,21 @@ packages:
name: matcher name: matcher
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.12.10-nullsafety" version: "0.12.10-nullsafety.1"
meta: meta:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0-nullsafety.2" version: "1.3.0-nullsafety.3"
path: path:
dependency: transitive dependency: transitive
description: description:
name: path name: path
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.8.0-nullsafety" version: "1.8.0-nullsafety.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@@ -99,56 +99,56 @@ packages:
name: source_span name: source_span
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.8.0-nullsafety" version: "1.8.0-nullsafety.2"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
name: stack_trace name: stack_trace
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.10.0-nullsafety" version: "1.10.0-nullsafety.2"
stream_channel: stream_channel:
dependency: transitive dependency: transitive
description: description:
name: stream_channel name: stream_channel
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.0-nullsafety" version: "2.1.0-nullsafety.1"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
name: string_scanner name: string_scanner
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.1.0-nullsafety" version: "1.1.0-nullsafety.1"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
name: term_glyph name: term_glyph
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.2.0-nullsafety" version: "1.2.0-nullsafety.1"
test_api: test_api:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.2.19-nullsafety" version: "0.2.19-nullsafety.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
name: typed_data name: typed_data
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "1.3.0-nullsafety.2" version: "1.3.0-nullsafety.3"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "2.1.0-nullsafety.2" version: "2.1.0-nullsafety.3"
sdks: sdks:
dart: ">=2.10.0-0.0.dev <2.10.0" dart: ">=2.10.0-110 <=2.11.0-181.0.dev"
flutter: ">=1.20.0 <2.0.0" flutter: ">=1.20.0 <2.0.0"

View File

@@ -1,6 +1,6 @@
name: flutter_qjs 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! description: This plugin is a simple js engine for flutter using the `quickjs` project. Plugin currently supports all the platforms except web!
version: 0.1.1 version: 0.1.2
homepage: https://github.com/ekibun/flutter_qjs homepage: https://github.com/ekibun/flutter_qjs
environment: environment:

View File

@@ -3,7 +3,7 @@
* @Author: ekibun * @Author: ekibun
* @Date: 2020-09-06 13:02:46 * @Date: 2020-09-06 13:02:46
* @LastEditors: ekibun * @LastEditors: ekibun
* @LastEditTime: 2020-10-02 17:27:52 * @LastEditTime: 2020-10-03 21:36:06
*/ */
import 'dart:convert'; import 'dart:convert';
import 'dart:io'; import 'dart:io';
@@ -67,7 +67,7 @@ void main() async {
(...args)=>`hello \${args}!`, a, (...args)=>`hello \${args}!`, a,
0.1, true, false, 1, "world", module 0.1, true, false, 1, "world", module
])); ]));
""", "<eval>"); """, name: "<eval>");
print(value); print(value);
print(await value[0]('world')); print(await value[0]('world'));
qjs.close(); qjs.close();