remove dart object when jsfree.

This commit is contained in:
ekibun
2021-01-18 13:25:16 +08:00
parent 7de32aac0b
commit d51290b2ea
9 changed files with 151 additions and 43 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.2.4 version: 0.2.5
homepage: https://github.com/ekibun/flutter_qjs homepage: https://github.com/ekibun/flutter_qjs
environment: environment: