mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 13:27:24 +00:00
remove dart object when jsfree.
This commit is contained in:
@@ -6,6 +6,10 @@
|
|||||||
* @LastEditTime: 2020-12-02 11:36:40
|
* @LastEditTime: 2020-12-02 11:36:40
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
## 0.2.5
|
||||||
|
|
||||||
|
* remove dart object when jsfree.
|
||||||
|
|
||||||
## 0.2.4
|
## 0.2.4
|
||||||
|
|
||||||
* wrap dart object to js.
|
* wrap dart object to js.
|
||||||
|
46
cxx/ffi.cpp
46
cxx/ffi.cpp
@@ -39,7 +39,7 @@ extern "C"
|
|||||||
{
|
{
|
||||||
JSRuntime *rt = JS_GetRuntime(ctx);
|
JSRuntime *rt = JS_GetRuntime(ctx);
|
||||||
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt);
|
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt);
|
||||||
const char *str = (char *)channel(ctx, (char *)0, (void *)module_name);
|
const char *str = (char *)channel(ctx, module_name, nullptr);
|
||||||
if (str == 0)
|
if (str == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
JSValue func_val = JS_Eval(ctx, str, strlen(str), module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
JSValue func_val = JS_Eval(ctx, str, strlen(str), module_name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
||||||
@@ -71,6 +71,50 @@ extern "C"
|
|||||||
return rt;
|
return rt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLLEXPORT uint32_t jsNewClass(JSContext *ctx, const char *name)
|
||||||
|
{
|
||||||
|
JSClassID QJSClassId = 0;
|
||||||
|
JS_NewClassID(&QJSClassId);
|
||||||
|
JSRuntime *rt = JS_GetRuntime(ctx);
|
||||||
|
if (!JS_IsRegisteredClass(rt, QJSClassId))
|
||||||
|
{
|
||||||
|
JSClassDef def{
|
||||||
|
name,
|
||||||
|
// destructor
|
||||||
|
[](JSRuntime *rt, JSValue obj) noexcept {
|
||||||
|
JSClassID classid = JS_GetClassID(obj);
|
||||||
|
ObjectOpaque *opaque = (ObjectOpaque *)JS_GetOpaque(obj, classid);
|
||||||
|
JSChannel *channel = (JSChannel *)JS_GetRuntimeOpaque(rt);
|
||||||
|
channel((JSContext *)rt, nullptr, (void *)opaque->opaque);
|
||||||
|
delete opaque;
|
||||||
|
}};
|
||||||
|
int e = JS_NewClass(rt, QJSClassId, &def);
|
||||||
|
if (e < 0)
|
||||||
|
{
|
||||||
|
JS_ThrowInternalError(ctx, "Cant register class %s", name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QJSClassId;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT void *jsGetObjectOpaque(JSValue *obj, uint32_t classid)
|
||||||
|
{
|
||||||
|
ObjectOpaque *opaque = (ObjectOpaque *)JS_GetOpaque(*obj, classid);
|
||||||
|
if(opaque == nullptr) return nullptr;
|
||||||
|
return opaque->opaque;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT JSValue *jsNewObjectClass(JSContext *ctx, uint32_t QJSClassId, void *opaque)
|
||||||
|
{
|
||||||
|
auto jsobj = new JSValue(JS_NewObjectClass(ctx, QJSClassId));
|
||||||
|
if (JS_IsException(*jsobj))
|
||||||
|
return jsobj;
|
||||||
|
ObjectOpaque *objOpaque = new ObjectOpaque{ctx, opaque};
|
||||||
|
JS_SetOpaque(*jsobj, objOpaque);
|
||||||
|
return jsobj;
|
||||||
|
}
|
||||||
|
|
||||||
DLLEXPORT void jsSetMaxStackSize(JSRuntime *rt, size_t stack_size)
|
DLLEXPORT void jsSetMaxStackSize(JSRuntime *rt, size_t stack_size)
|
||||||
{
|
{
|
||||||
JS_SetMaxStackSize(rt, stack_size);
|
JS_SetMaxStackSize(rt, stack_size);
|
||||||
|
11
cxx/ffi.h
11
cxx/ffi.h
@@ -8,6 +8,11 @@
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
struct ObjectOpaque
|
||||||
|
{
|
||||||
|
JSContext *ctx;
|
||||||
|
void *opaque;
|
||||||
|
};
|
||||||
|
|
||||||
typedef void *JSChannel(JSContext *ctx, const char *method, void *argv);
|
typedef void *JSChannel(JSContext *ctx, const char *method, void *argv);
|
||||||
|
|
||||||
@@ -21,6 +26,12 @@ extern "C"
|
|||||||
|
|
||||||
DLLEXPORT JSRuntime *jsNewRuntime(JSChannel channel);
|
DLLEXPORT JSRuntime *jsNewRuntime(JSChannel channel);
|
||||||
|
|
||||||
|
DLLEXPORT uint32_t jsNewClass(JSContext *ctx, const char *name);
|
||||||
|
|
||||||
|
DLLEXPORT void *jsGetObjectOpaque(JSValue *obj, uint32_t classid);
|
||||||
|
|
||||||
|
DLLEXPORT JSValue *jsNewObjectClass(JSContext *ctx, uint32_t QJSClassId, void *opaque);
|
||||||
|
|
||||||
DLLEXPORT void jsSetMaxStackSize(JSRuntime *rt, size_t stack_size);
|
DLLEXPORT void jsSetMaxStackSize(JSRuntime *rt, size_t stack_size);
|
||||||
|
|
||||||
DLLEXPORT void jsFreeRuntime(JSRuntime *rt);
|
DLLEXPORT void jsFreeRuntime(JSRuntime *rt);
|
||||||
|
Submodule cxx/quickjs updated: 9ac874168b...7daa190ca7
@@ -82,7 +82,7 @@ packages:
|
|||||||
path: ".."
|
path: ".."
|
||||||
relative: true
|
relative: true
|
||||||
source: path
|
source: path
|
||||||
version: "0.2.4"
|
version: "0.2.5"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
|
59
lib/ffi.dart
59
lib/ffi.dart
@@ -113,13 +113,14 @@ class RuntimeOpaque {
|
|||||||
List<JSRef> ref = [];
|
List<JSRef> ref = [];
|
||||||
ReceivePort port;
|
ReceivePort port;
|
||||||
Future Function(Pointer) promiseToFuture;
|
Future Function(Pointer) promiseToFuture;
|
||||||
Pointer Function(Object) objectWrapper;
|
int dartObjectClassId;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<Pointer, RuntimeOpaque> runtimeOpaques = Map();
|
final Map<Pointer, RuntimeOpaque> runtimeOpaques = Map();
|
||||||
|
|
||||||
Pointer channelDispacher(Pointer ctx, Pointer method, Pointer argv) {
|
Pointer channelDispacher(Pointer ctx, Pointer method, Pointer argv) {
|
||||||
return runtimeOpaques[jsGetRuntime(ctx)].channel(ctx, method, argv);
|
Pointer rt = method.address == 0 ? ctx : jsGetRuntime(ctx);
|
||||||
|
return runtimeOpaques[rt]?.channel(ctx, method, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
Pointer jsNewRuntime(
|
Pointer jsNewRuntime(
|
||||||
@@ -518,6 +519,60 @@ String jsToCString(
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DLLEXPORT uint32_t jsNewClass(JSContext *ctx, const char *name)
|
||||||
|
final int Function(
|
||||||
|
Pointer ctx,
|
||||||
|
Pointer<Utf8> name,
|
||||||
|
) _jsNewClass = qjsLib
|
||||||
|
.lookup<
|
||||||
|
NativeFunction<
|
||||||
|
Uint32 Function(
|
||||||
|
Pointer,
|
||||||
|
Pointer<Utf8>,
|
||||||
|
)>>("jsNewClass")
|
||||||
|
.asFunction();
|
||||||
|
|
||||||
|
int jsNewClass(
|
||||||
|
Pointer ctx,
|
||||||
|
String name,
|
||||||
|
) {
|
||||||
|
var utf8name = Utf8.toUtf8(name);
|
||||||
|
var val = _jsNewClass(
|
||||||
|
ctx,
|
||||||
|
utf8name,
|
||||||
|
);
|
||||||
|
free(utf8name);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DLLEXPORT JSValue *jsNewObjectClass(JSContext *ctx, uint32_t QJSClassId, void *opaque)
|
||||||
|
final Pointer Function(
|
||||||
|
Pointer ctx,
|
||||||
|
int classId,
|
||||||
|
int opaque,
|
||||||
|
) jsNewObjectClass = qjsLib
|
||||||
|
.lookup<
|
||||||
|
NativeFunction<
|
||||||
|
Pointer Function(
|
||||||
|
Pointer,
|
||||||
|
Uint32,
|
||||||
|
IntPtr,
|
||||||
|
)>>("jsNewObjectClass")
|
||||||
|
.asFunction();
|
||||||
|
|
||||||
|
/// DLLEXPORT void *jsGetObjectOpaque(JSValue *obj, uint32_t classid)
|
||||||
|
final int Function(
|
||||||
|
Pointer obj,
|
||||||
|
int classid,
|
||||||
|
) jsGetObjectOpaque = qjsLib
|
||||||
|
.lookup<
|
||||||
|
NativeFunction<
|
||||||
|
IntPtr Function(
|
||||||
|
Pointer,
|
||||||
|
Uint32,
|
||||||
|
)>>("jsGetObjectOpaque")
|
||||||
|
.asFunction();
|
||||||
|
|
||||||
/// uint8_t *jsGetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst *obj)
|
/// uint8_t *jsGetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst *obj)
|
||||||
final Pointer<Uint8> Function(
|
final Pointer<Uint8> Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
|
@@ -44,7 +44,14 @@ class FlutterQjs {
|
|||||||
if (_rt != null) return;
|
if (_rt != null) return;
|
||||||
_rt = jsNewRuntime((ctx, method, argv) {
|
_rt = jsNewRuntime((ctx, method, argv) {
|
||||||
try {
|
try {
|
||||||
if (method.address != 0) {
|
if (method.address == 0) {
|
||||||
|
Pointer rt = ctx;
|
||||||
|
DartObject obj = DartObject.fromAddress(rt, argv.address);
|
||||||
|
obj?.release();
|
||||||
|
runtimeOpaques[rt]?.ref?.remove(obj);
|
||||||
|
return Pointer.fromAddress(0);
|
||||||
|
}
|
||||||
|
if (argv.address != 0 && method.address != 0) {
|
||||||
if (methodHandler == null) throw Exception("No MethodHandler");
|
if (methodHandler == null) throw Exception("No MethodHandler");
|
||||||
var argvs = jsToDart(ctx, argv);
|
var argvs = jsToDart(ctx, argv);
|
||||||
return dartToJs(
|
return dartToJs(
|
||||||
@@ -55,7 +62,8 @@ class FlutterQjs {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
if (moduleHandler == null) throw Exception("No ModuleHandler");
|
if (moduleHandler == null) throw Exception("No ModuleHandler");
|
||||||
var ret = Utf8.toUtf8(moduleHandler(Utf8.fromUtf8(argv.cast<Utf8>())));
|
var ret =
|
||||||
|
Utf8.toUtf8(moduleHandler(Utf8.fromUtf8(method.cast<Utf8>())));
|
||||||
Future.microtask(() {
|
Future.microtask(() {
|
||||||
free(ret);
|
free(ret);
|
||||||
});
|
});
|
||||||
|
@@ -44,15 +44,16 @@ class DartObject implements JSRef {
|
|||||||
runtimeOpaques[jsGetRuntime(ctx)]?.ref?.add(this);
|
runtimeOpaques[jsGetRuntime(ctx)]?.ref?.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DartObject fromAddress(Pointer ctx, int val) {
|
static DartObject fromAddress(Pointer rt, int val) {
|
||||||
return runtimeOpaques[jsGetRuntime(ctx)]?.ref?.firstWhere(
|
return runtimeOpaques[rt]?.ref?.firstWhere(
|
||||||
(e) => identityHashCode(e) == val,
|
(e) => identityHashCode(e) == val,
|
||||||
orElse: null,
|
orElse: () => null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void release() {
|
void release() {
|
||||||
|
obj = null;
|
||||||
ctx = null;
|
ctx = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,7 +217,14 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return runtimeOpaques[jsGetRuntime(ctx)]?.objectWrapper(val) ?? jsUNDEFINED();
|
int dartObjectClassId =
|
||||||
|
runtimeOpaques[jsGetRuntime(ctx)]?.dartObjectClassId ?? 0;
|
||||||
|
if (dartObjectClassId == 0) return jsUNDEFINED();
|
||||||
|
return jsNewObjectClass(
|
||||||
|
ctx,
|
||||||
|
dartObjectClassId,
|
||||||
|
identityHashCode(DartObject(ctx, val)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
||||||
@@ -233,6 +241,13 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
|||||||
case JSTag.STRING:
|
case JSTag.STRING:
|
||||||
return jsToCString(ctx, val);
|
return jsToCString(ctx, val);
|
||||||
case JSTag.OBJECT:
|
case JSTag.OBJECT:
|
||||||
|
final rt = jsGetRuntime(ctx);
|
||||||
|
final dartObjectClassId = runtimeOpaques[rt].dartObjectClassId;
|
||||||
|
if (dartObjectClassId != 0) {
|
||||||
|
final dartObject = DartObject.fromAddress(
|
||||||
|
rt, jsGetObjectOpaque(val, dartObjectClassId));
|
||||||
|
if (dartObject != null) return dartObject.obj;
|
||||||
|
}
|
||||||
Pointer<IntPtr> psize = allocate<IntPtr>();
|
Pointer<IntPtr> psize = allocate<IntPtr>();
|
||||||
Pointer<Uint8> buf = jsGetArrayBuffer(ctx, psize, val);
|
Pointer<Uint8> buf = jsGetArrayBuffer(ctx, psize, val);
|
||||||
int size = psize.value;
|
int size = psize.value;
|
||||||
@@ -247,7 +262,7 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
|||||||
if (jsIsFunction(ctx, val) != 0) {
|
if (jsIsFunction(ctx, val) != 0) {
|
||||||
return JSFunction(ctx, val);
|
return JSFunction(ctx, val);
|
||||||
} else if (jsIsPromise(ctx, val) != 0) {
|
} else if (jsIsPromise(ctx, val) != 0) {
|
||||||
return runtimeOpaques[jsGetRuntime(ctx)]?.promiseToFuture(val);
|
return runtimeOpaques[rt]?.promiseToFuture(val);
|
||||||
} else if (jsIsArray(ctx, val) != 0) {
|
} else if (jsIsArray(ctx, val) != 0) {
|
||||||
Pointer jslength = jsGetPropertyStr(ctx, val, "length");
|
Pointer jslength = jsGetPropertyStr(ctx, val, "length");
|
||||||
int length = jsToInt64(ctx, jslength);
|
int length = jsToInt64(ctx, jslength);
|
||||||
@@ -283,10 +298,6 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
|||||||
}
|
}
|
||||||
jsFree(ctx, ptab.value);
|
jsFree(ctx, ptab.value);
|
||||||
free(ptab);
|
free(ptab);
|
||||||
final objHash = ret["__dart_obj_hash__"];
|
|
||||||
if (objHash is int) {
|
|
||||||
return DartObject.fromAddress(ctx, objHash)?.obj ?? ret;
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -318,23 +329,9 @@ Pointer jsNewContextWithPromsieWrapper(Pointer rt) {
|
|||||||
""",
|
""",
|
||||||
"<future>",
|
"<future>",
|
||||||
JSEvalFlag.GLOBAL);
|
JSEvalFlag.GLOBAL);
|
||||||
var jsObjectWrapper = jsEval(
|
runtimeOpaque.dartObjectClassId = jsNewClass(ctx, "DartObject");
|
||||||
ctx,
|
|
||||||
"""
|
|
||||||
(objHash, type) => {
|
|
||||||
const ret = {
|
|
||||||
"__dart_obj_hash__": objHash,
|
|
||||||
};
|
|
||||||
ret.__proto__.toString = ()=> "" + type;
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
""",
|
|
||||||
"<future>",
|
|
||||||
JSEvalFlag.GLOBAL);
|
|
||||||
final promiseWrapper = JSRefValue(ctx, jsPromiseWrapper);
|
final promiseWrapper = JSRefValue(ctx, jsPromiseWrapper);
|
||||||
jsFreeValue(ctx, jsPromiseWrapper);
|
jsFreeValue(ctx, jsPromiseWrapper);
|
||||||
final objectWrapper = JSRefValue(ctx, jsObjectWrapper);
|
|
||||||
jsFreeValue(ctx, jsObjectWrapper);
|
|
||||||
runtimeOpaque.promiseToFuture = (promise) {
|
runtimeOpaque.promiseToFuture = (promise) {
|
||||||
var completer = Completer();
|
var completer = Completer();
|
||||||
var wrapper = promiseWrapper.val;
|
var wrapper = promiseWrapper.val;
|
||||||
@@ -345,16 +342,5 @@ Pointer jsNewContextWithPromsieWrapper(Pointer rt) {
|
|||||||
jsFreeValue(ctx, jsPromise);
|
jsFreeValue(ctx, jsPromise);
|
||||||
return wrapPromise.completer.future;
|
return wrapPromise.completer.future;
|
||||||
};
|
};
|
||||||
runtimeOpaque.objectWrapper = (obj) {
|
|
||||||
var jsObjHash = jsNewInt64(ctx, identityHashCode(DartObject(ctx, obj)));
|
|
||||||
var jsTypeString = jsNewString(ctx, obj.toString());
|
|
||||||
var wrapper = objectWrapper.val;
|
|
||||||
if (wrapper == null) throw Exception("Runtime has been released!");
|
|
||||||
var ret = jsCall(ctx, wrapper, null, [jsObjHash, jsTypeString]);
|
|
||||||
jsFreeValue(ctx, jsObjHash);
|
|
||||||
jsFreeValue(ctx, jsTypeString);
|
|
||||||
return ret;
|
|
||||||
};
|
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
@@ -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.2.4
|
version: 0.2.5
|
||||||
homepage: https://github.com/ekibun/flutter_qjs
|
homepage: https://github.com/ekibun/flutter_qjs
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
Reference in New Issue
Block a user