mirror of
https://github.com/wgh136/flutter_qjs.git
synced 2025-09-27 13:27:24 +00:00
v0.3.2
This commit is contained in:
@@ -9,6 +9,7 @@
|
|||||||
## 0.3.2
|
## 0.3.2
|
||||||
|
|
||||||
* fix Promise reject cannot get Exception string.
|
* fix Promise reject cannot get Exception string.
|
||||||
|
* wrap JSError.
|
||||||
|
|
||||||
## 0.3.1
|
## 0.3.1
|
||||||
|
|
||||||
|
@@ -56,6 +56,7 @@ Data conversion between dart and js are implemented as follow:
|
|||||||
| Map | Object |
|
| Map | Object |
|
||||||
| Function<br>JSInvokable | function(....args) |
|
| Function<br>JSInvokable | function(....args) |
|
||||||
| Future | Promise |
|
| Future | Promise |
|
||||||
|
| JSError | Error |
|
||||||
| Object | DartObject |
|
| Object | DartObject |
|
||||||
|
|
||||||
**notice:** `JSInvokable` does not extend `Function`, but can be used same as `Function`.
|
**notice:** `JSInvokable` does not extend `Function`, but can be used same as `Function`.
|
||||||
|
14
cxx/ffi.cpp
14
cxx/ffi.cpp
@@ -13,9 +13,9 @@
|
|||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
|
||||||
DLLEXPORT JSValue *jsThrowInternalError(JSContext *ctx, char *message)
|
DLLEXPORT JSValue *jsThrow(JSContext *ctx, JSValue *obj)
|
||||||
{
|
{
|
||||||
return new JSValue(JS_ThrowInternalError(ctx, "%s", message));
|
return new JSValue(JS_Throw(ctx, *obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT JSValue *jsEXCEPTION()
|
DLLEXPORT JSValue *jsEXCEPTION()
|
||||||
@@ -293,6 +293,16 @@ extern "C"
|
|||||||
return JS_IsArray(ctx, *val);
|
return JS_IsArray(ctx, *val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLLEXPORT int32_t jsIsError(JSContext *ctx, JSValueConst *val)
|
||||||
|
{
|
||||||
|
return JS_IsError(ctx, *val);
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT JSValue *jsNewError(JSContext *ctx)
|
||||||
|
{
|
||||||
|
return new JSValue(JS_NewError(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
DLLEXPORT JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
|
DLLEXPORT JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
|
||||||
JSAtom prop)
|
JSAtom prop)
|
||||||
{
|
{
|
||||||
|
@@ -17,7 +17,7 @@ extern "C"
|
|||||||
|
|
||||||
typedef void *JSChannel(JSContext *ctx, size_t type, void *argv);
|
typedef void *JSChannel(JSContext *ctx, size_t type, void *argv);
|
||||||
|
|
||||||
DLLEXPORT JSValue *jsThrowInternalError(JSContext *ctx, char *message);
|
DLLEXPORT JSValue *jsThrow(JSContext *ctx, JSValue *obj);
|
||||||
|
|
||||||
DLLEXPORT JSValue *jsEXCEPTION();
|
DLLEXPORT JSValue *jsEXCEPTION();
|
||||||
|
|
||||||
@@ -95,6 +95,10 @@ extern "C"
|
|||||||
|
|
||||||
DLLEXPORT int32_t jsIsArray(JSContext *ctx, JSValueConst *val);
|
DLLEXPORT int32_t jsIsArray(JSContext *ctx, JSValueConst *val);
|
||||||
|
|
||||||
|
DLLEXPORT int32_t jsIsError(JSContext *ctx, JSValueConst *val);
|
||||||
|
|
||||||
|
DLLEXPORT JSValue *jsNewError(JSContext *ctx);
|
||||||
|
|
||||||
DLLEXPORT JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
|
DLLEXPORT JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
|
||||||
JSAtom prop);
|
JSAtom prop);
|
||||||
|
|
||||||
|
52
lib/ffi.dart
52
lib/ffi.dart
@@ -67,26 +67,19 @@ final DynamicLibrary _qjsLib = Platform.environment['FLUTTER_TEST'] == 'true'
|
|||||||
? DynamicLibrary.open('libqjs.so')
|
? DynamicLibrary.open('libqjs.so')
|
||||||
: DynamicLibrary.process());
|
: DynamicLibrary.process());
|
||||||
|
|
||||||
/// JSValue *jsThrowInternalError(JSContext *ctx, char *message)
|
/// DLLEXPORT JSValue *jsThrow(JSContext *ctx, JSValue *obj)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
Pointer ctx,
|
Pointer ctx,
|
||||||
Pointer<Utf8> message,
|
Pointer obj,
|
||||||
) _jsThrowInternalError = _qjsLib
|
) jsThrow = _qjsLib
|
||||||
.lookup<
|
.lookup<
|
||||||
NativeFunction<
|
NativeFunction<
|
||||||
Pointer Function(
|
Pointer Function(
|
||||||
Pointer,
|
Pointer,
|
||||||
Pointer<Utf8>,
|
Pointer,
|
||||||
)>>('jsThrowInternalError')
|
)>>('jsThrow')
|
||||||
.asFunction();
|
.asFunction();
|
||||||
|
|
||||||
Pointer jsThrowInternalError(Pointer ctx, String message) {
|
|
||||||
var utf8message = Utf8.toUtf8(message);
|
|
||||||
var val = _jsThrowInternalError(ctx, utf8message);
|
|
||||||
free(utf8message);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// JSValue *jsEXCEPTION()
|
/// JSValue *jsEXCEPTION()
|
||||||
final Pointer Function() jsEXCEPTION = _qjsLib
|
final Pointer Function() jsEXCEPTION = _qjsLib
|
||||||
.lookup<NativeFunction<Pointer Function()>>('jsEXCEPTION')
|
.lookup<NativeFunction<Pointer Function()>>('jsEXCEPTION')
|
||||||
@@ -117,6 +110,7 @@ class RuntimeOpaque {
|
|||||||
List<JSRef> ref = [];
|
List<JSRef> ref = [];
|
||||||
ReceivePort port;
|
ReceivePort port;
|
||||||
int dartObjectClassId;
|
int dartObjectClassId;
|
||||||
|
int jsExceptionClassId;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<Pointer, RuntimeOpaque> runtimeOpaques = Map();
|
final Map<Pointer, RuntimeOpaque> runtimeOpaques = Map();
|
||||||
@@ -164,10 +158,11 @@ final void Function(
|
|||||||
void jsFreeRuntime(
|
void jsFreeRuntime(
|
||||||
Pointer rt,
|
Pointer rt,
|
||||||
) {
|
) {
|
||||||
runtimeOpaques[rt]?.ref?.forEach((val) {
|
while (0 < runtimeOpaques[rt]?.ref?.length ?? 0) {
|
||||||
val.release();
|
final ref = runtimeOpaques[rt]?.ref?.first;
|
||||||
});
|
ref.release();
|
||||||
runtimeOpaques.remove(rt);
|
runtimeOpaques[rt]?.ref?.remove(ref);
|
||||||
|
}
|
||||||
_jsFreeRuntime(rt);
|
_jsFreeRuntime(rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +195,7 @@ Pointer jsNewContext(Pointer rt) {
|
|||||||
final runtimeOpaque = runtimeOpaques[rt];
|
final runtimeOpaque = runtimeOpaques[rt];
|
||||||
if (runtimeOpaque == null) throw Exception('Runtime has been released!');
|
if (runtimeOpaque == null) throw Exception('Runtime has been released!');
|
||||||
runtimeOpaque.dartObjectClassId = jsNewClass(ctx, 'DartObject');
|
runtimeOpaque.dartObjectClassId = jsNewClass(ctx, 'DartObject');
|
||||||
|
runtimeOpaque.jsExceptionClassId = jsNewClass(ctx, 'JSException');
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,6 +647,30 @@ final int Function(
|
|||||||
)>>('jsIsArray')
|
)>>('jsIsArray')
|
||||||
.asFunction();
|
.asFunction();
|
||||||
|
|
||||||
|
/// DLLEXPORT int32_t jsIsError(JSContext *ctx, JSValueConst *val);
|
||||||
|
final int Function(
|
||||||
|
Pointer ctx,
|
||||||
|
Pointer val,
|
||||||
|
) jsIsError = _qjsLib
|
||||||
|
.lookup<
|
||||||
|
NativeFunction<
|
||||||
|
Int32 Function(
|
||||||
|
Pointer,
|
||||||
|
Pointer,
|
||||||
|
)>>('jsIsError')
|
||||||
|
.asFunction();
|
||||||
|
|
||||||
|
/// DLLEXPORT JSValue *jsNewError(JSContext *ctx);
|
||||||
|
final Pointer Function(
|
||||||
|
Pointer ctx,
|
||||||
|
) jsNewError = _qjsLib
|
||||||
|
.lookup<
|
||||||
|
NativeFunction<
|
||||||
|
Pointer Function(
|
||||||
|
Pointer,
|
||||||
|
)>>('jsNewError')
|
||||||
|
.asFunction();
|
||||||
|
|
||||||
/// JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
|
/// JSValue *jsGetProperty(JSContext *ctx, JSValueConst *this_obj,
|
||||||
/// JSAtom prop)
|
/// JSAtom prop)
|
||||||
final Pointer Function(
|
final Pointer Function(
|
||||||
|
@@ -50,25 +50,24 @@ class FlutterQjs {
|
|||||||
case JSChannelType.METHON:
|
case JSChannelType.METHON:
|
||||||
final pdata = ptr.cast<Pointer>();
|
final pdata = ptr.cast<Pointer>();
|
||||||
final argc = pdata.elementAt(1).value.cast<Int32>().value;
|
final argc = pdata.elementAt(1).value.cast<Int32>().value;
|
||||||
List pargs = <Pointer>[];
|
List pargs = [];
|
||||||
for (var i = 0; i < argc; i++) {
|
for (var i = 0; i < argc; i++) {
|
||||||
pargs.add(Pointer.fromAddress(
|
pargs.add(jsToDart(
|
||||||
|
ctx,
|
||||||
|
Pointer.fromAddress(
|
||||||
pdata.elementAt(2).value.address + sizeOfJSValue * i,
|
pdata.elementAt(2).value.address + sizeOfJSValue * i,
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
final pThis = pdata.elementAt(0).value;
|
|
||||||
JSInvokable func = jsToDart(ctx, pdata.elementAt(3).value);
|
JSInvokable func = jsToDart(ctx, pdata.elementAt(3).value);
|
||||||
if (func is NativeJSInvokable) {
|
|
||||||
return dartToJs(ctx, func.invokeNative(ctx, pThis, pargs));
|
|
||||||
}
|
|
||||||
return dartToJs(
|
return dartToJs(
|
||||||
ctx,
|
ctx,
|
||||||
func.invoke(
|
func.invoke(
|
||||||
pargs.map((e) => jsToDart(ctx, e)).toList(),
|
pargs,
|
||||||
jsToDart(ctx, pThis),
|
jsToDart(ctx, pdata.elementAt(0).value),
|
||||||
));
|
));
|
||||||
case JSChannelType.MODULE:
|
case JSChannelType.MODULE:
|
||||||
if (moduleHandler == null) throw Exception('No ModuleHandler');
|
if (moduleHandler == null) throw JSError('No ModuleHandler');
|
||||||
var ret = Utf8.toUtf8(moduleHandler(
|
var ret = Utf8.toUtf8(moduleHandler(
|
||||||
Utf8.fromUtf8(ptr.cast<Utf8>()),
|
Utf8.fromUtf8(ptr.cast<Utf8>()),
|
||||||
));
|
));
|
||||||
@@ -79,7 +78,7 @@ class FlutterQjs {
|
|||||||
case JSChannelType.PROMISE_TRACK:
|
case JSChannelType.PROMISE_TRACK:
|
||||||
final errStr = parseJSException(ctx, ptr);
|
final errStr = parseJSException(ctx, ptr);
|
||||||
if (hostPromiseRejectionHandler != null) {
|
if (hostPromiseRejectionHandler != null) {
|
||||||
hostPromiseRejectionHandler(errStr);
|
hostPromiseRejectionHandler(errStr.toString());
|
||||||
} else {
|
} else {
|
||||||
print('unhandled promise rejection: $errStr');
|
print('unhandled promise rejection: $errStr');
|
||||||
}
|
}
|
||||||
@@ -91,21 +90,19 @@ class FlutterQjs {
|
|||||||
runtimeOpaques[rt]?.ref?.remove(obj);
|
runtimeOpaques[rt]?.ref?.remove(obj);
|
||||||
return Pointer.fromAddress(0);
|
return Pointer.fromAddress(0);
|
||||||
}
|
}
|
||||||
throw Exception('call channel with wrong type');
|
throw JSError('call channel with wrong type');
|
||||||
} catch (e, stack) {
|
} catch (e) {
|
||||||
final errStr = e.toString() + '\n' + stack.toString();
|
|
||||||
if (type == JSChannelType.FREE_OBJECT) {
|
if (type == JSChannelType.FREE_OBJECT) {
|
||||||
print('DartObject release error: ' + errStr);
|
print('DartObject release error: $e');
|
||||||
return Pointer.fromAddress(0);
|
return Pointer.fromAddress(0);
|
||||||
}
|
}
|
||||||
if (type == JSChannelType.MODULE) {
|
if (type == JSChannelType.MODULE) {
|
||||||
print('host Promise Rejection Handler error: ' + errStr);
|
print('host Promise Rejection Handler error: $e');
|
||||||
return Pointer.fromAddress(0);
|
return Pointer.fromAddress(0);
|
||||||
}
|
}
|
||||||
var err = jsThrowInternalError(
|
var throwObj = dartToJs(ctx, e);
|
||||||
ctx,
|
var err = jsThrow(ctx, throwObj);
|
||||||
errStr,
|
jsFreeValue(ctx, throwObj);
|
||||||
);
|
|
||||||
if (type == JSChannelType.MODULE) {
|
if (type == JSChannelType.MODULE) {
|
||||||
jsFreeValue(ctx, err);
|
jsFreeValue(ctx, err);
|
||||||
return Pointer.fromAddress(0);
|
return Pointer.fromAddress(0);
|
||||||
|
@@ -35,7 +35,7 @@ void _runJsIsolate(Map spawnMessage) async {
|
|||||||
'ptr': ptr.address,
|
'ptr': ptr.address,
|
||||||
});
|
});
|
||||||
while (ptr.value.address == 0) sleep(Duration.zero);
|
while (ptr.value.address == 0) sleep(Duration.zero);
|
||||||
if (ptr.value.address == -1) throw Exception('Module Not found');
|
if (ptr.value.address == -1) throw JSError('Module Not found');
|
||||||
var ret = Utf8.fromUtf8(ptr.value);
|
var ret = Utf8.fromUtf8(ptr.value);
|
||||||
sendPort.send({
|
sendPort.send({
|
||||||
'type': 'release',
|
'type': 'release',
|
||||||
@@ -76,10 +76,10 @@ void _runJsIsolate(Map spawnMessage) async {
|
|||||||
msgPort.send({
|
msgPort.send({
|
||||||
'data': encodeData(data),
|
'data': encodeData(data),
|
||||||
});
|
});
|
||||||
} catch (e, stack) {
|
} catch (e) {
|
||||||
if (msgPort != null)
|
if (msgPort != null)
|
||||||
msgPort.send({
|
msgPort.send({
|
||||||
'error': e.toString() + '\n' + stack.toString(),
|
'error': encodeData(e),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -137,11 +137,8 @@ class IsolateQjs {
|
|||||||
} else {
|
} else {
|
||||||
print('unhandled promise rejection: $errStr');
|
print('unhandled promise rejection: $errStr');
|
||||||
}
|
}
|
||||||
} catch (e, stack) {
|
} catch (e) {
|
||||||
print('host Promise Rejection Handler error: ' +
|
print('host Promise Rejection Handler error: $e');
|
||||||
e.toString() +
|
|
||||||
'\n' +
|
|
||||||
stack.toString());
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'module':
|
case 'module':
|
||||||
@@ -197,6 +194,6 @@ class IsolateQjs {
|
|||||||
if (result.containsKey('data')) {
|
if (result.containsKey('data')) {
|
||||||
return decodeData(result['data'], sendPort);
|
return decodeData(result['data'], sendPort);
|
||||||
} else
|
} else
|
||||||
throw result['error'];
|
throw decodeData(result['error'], sendPort);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
116
lib/wrapper.dart
116
lib/wrapper.dart
@@ -12,6 +12,37 @@ import 'dart:typed_data';
|
|||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'ffi.dart';
|
import 'ffi.dart';
|
||||||
|
|
||||||
|
class JSError {
|
||||||
|
String message;
|
||||||
|
String stack;
|
||||||
|
JSError(message, [stack]) {
|
||||||
|
if (message is JSError) {
|
||||||
|
this.message = message.message;
|
||||||
|
this.stack = message.stack;
|
||||||
|
} else {
|
||||||
|
this.message = message.toString();
|
||||||
|
this.stack = (stack ?? StackTrace.current).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return stack == null ? message.toString() : "$message\n$stack";
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSError decode(Map obj) {
|
||||||
|
if (obj.containsKey('__js_error'))
|
||||||
|
return JSError(obj['__js_error'], obj['__js_error_stack']);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map encode() {
|
||||||
|
return {
|
||||||
|
'__js_error': message,
|
||||||
|
'__js_error_stack': stack,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
abstract class JSInvokable {
|
abstract class JSInvokable {
|
||||||
dynamic invoke(List args, [dynamic thisVal]);
|
dynamic invoke(List args, [dynamic thisVal]);
|
||||||
|
|
||||||
@@ -32,19 +63,19 @@ abstract class JSInvokable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NativeJSInvokable extends JSInvokable {
|
// class NativeJSInvokable extends JSInvokable {
|
||||||
dynamic Function(Pointer ctx, Pointer thisVal, List<Pointer> args) _func;
|
// dynamic Function(Pointer ctx, Pointer thisVal, List<Pointer> args) _func;
|
||||||
NativeJSInvokable(this._func);
|
// NativeJSInvokable(this._func);
|
||||||
|
|
||||||
@override
|
// @override
|
||||||
dynamic invoke(List args, [dynamic thisVal]) {
|
// dynamic invoke(List args, [dynamic thisVal]) {
|
||||||
throw UnimplementedError('use invokeNative instead.');
|
// throw UnimplementedError('use invokeNative instead.');
|
||||||
}
|
// }
|
||||||
|
|
||||||
invokeNative(Pointer ctx, Pointer thisVal, List<Pointer> args) {
|
// invokeNative(Pointer ctx, Pointer thisVal, List<Pointer> args) {
|
||||||
_func(ctx, thisVal, args);
|
// _func(ctx, thisVal, args);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
class _DartFunction extends JSInvokable {
|
class _DartFunction extends JSInvokable {
|
||||||
Function _func;
|
Function _func;
|
||||||
@@ -181,7 +212,7 @@ class IsolateJSFunction extends JSInvokable {
|
|||||||
if (result.containsKey('data'))
|
if (result.containsKey('data'))
|
||||||
return decodeData(result['data'], port);
|
return decodeData(result['data'], port);
|
||||||
else
|
else
|
||||||
throw result['error'];
|
throw decodeData(result['error'], port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,10 +236,10 @@ class IsolateFunction extends JSInvokable implements DartReleasable {
|
|||||||
msgPort.send({
|
msgPort.send({
|
||||||
'data': encodeData(data),
|
'data': encodeData(data),
|
||||||
});
|
});
|
||||||
} catch (e, stack) {
|
} catch (e) {
|
||||||
if (msgPort != null)
|
if (msgPort != null)
|
||||||
msgPort.send({
|
msgPort.send({
|
||||||
'error': e.toString() + '\n' + stack.toString(),
|
'error': encodeData(e),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -229,7 +260,7 @@ class IsolateFunction extends JSInvokable implements DartReleasable {
|
|||||||
if (result.containsKey('data'))
|
if (result.containsKey('data'))
|
||||||
return decodeData(result['data'], _port);
|
return decodeData(result['data'], _port);
|
||||||
else
|
else
|
||||||
throw result['error'];
|
throw decodeData(result['error'], _port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -242,6 +273,7 @@ class IsolateFunction extends JSInvokable implements DartReleasable {
|
|||||||
|
|
||||||
dynamic encodeData(dynamic data, {Map<dynamic, dynamic> cache}) {
|
dynamic encodeData(dynamic data, {Map<dynamic, dynamic> cache}) {
|
||||||
if (cache == null) cache = Map();
|
if (cache == null) cache = Map();
|
||||||
|
if (data is JSError) return data.encode();
|
||||||
if (cache.containsKey(data)) return cache[data];
|
if (cache.containsKey(data)) return cache[data];
|
||||||
if (data is List) {
|
if (data is List) {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
@@ -285,11 +317,10 @@ dynamic encodeData(dynamic data, {Map<dynamic, dynamic> cache}) {
|
|||||||
futurePort.close();
|
futurePort.close();
|
||||||
(port as SendPort).send({'data': encodeData(value)});
|
(port as SendPort).send({'data': encodeData(value)});
|
||||||
});
|
});
|
||||||
}, onError: (e, stack) {
|
}, onError: (e) {
|
||||||
futurePort.first.then((port) {
|
futurePort.first.then((port) {
|
||||||
futurePort.close();
|
futurePort.close();
|
||||||
(port as SendPort)
|
(port as SendPort).send({'error': encodeData(e)});
|
||||||
.send({'error': e.toString() + '\n' + stack.toString()});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
@@ -311,6 +342,8 @@ dynamic decodeData(dynamic data, SendPort port, {Map<dynamic, dynamic> cache}) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (data is Map) {
|
if (data is Map) {
|
||||||
|
final jsException = JSError.decode(data);
|
||||||
|
if (jsException != null) return jsException;
|
||||||
if (data.containsKey('__js_obj_val')) {
|
if (data.containsKey('__js_obj_val')) {
|
||||||
int ctx = data['__js_obj_ctx'];
|
int ctx = data['__js_obj_ctx'];
|
||||||
int val = data['__js_obj_val'];
|
int val = data['__js_obj_val'];
|
||||||
@@ -340,9 +373,9 @@ dynamic decodeData(dynamic data, SendPort port, {Map<dynamic, dynamic> cache}) {
|
|||||||
futurePort.first.then((value) {
|
futurePort.first.then((value) {
|
||||||
futurePort.close();
|
futurePort.close();
|
||||||
if (value['error'] != null) {
|
if (value['error'] != null) {
|
||||||
futureCompleter.completeError(value['error']);
|
futureCompleter.completeError(decodeData(value['error'], port));
|
||||||
} else {
|
} else {
|
||||||
futureCompleter.complete(value['data']);
|
futureCompleter.complete(decodeData(value['data'], port));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return futureCompleter.future;
|
return futureCompleter.future;
|
||||||
@@ -358,16 +391,13 @@ dynamic decodeData(dynamic data, SendPort port, {Map<dynamic, dynamic> cache}) {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
String parseJSException(Pointer ctx, [Pointer perr]) {
|
dynamic parseJSException(Pointer ctx, [Pointer perr]) {
|
||||||
final e = perr ?? jsGetException(ctx);
|
final e = perr ?? jsGetException(ctx);
|
||||||
|
var err;
|
||||||
var err = jsToCString(ctx, e);
|
try {
|
||||||
if (jsValueGetTag(e) == JSTag.OBJECT) {
|
err = jsToDart(ctx, e);
|
||||||
Pointer stack = jsGetPropertyValue(ctx, e, 'stack');
|
} catch (exception) {
|
||||||
if (jsToBool(ctx, stack) != 0) {
|
err = exception;
|
||||||
err += '\n' + jsToCString(ctx, stack);
|
|
||||||
}
|
|
||||||
jsFreeValue(ctx, stack);
|
|
||||||
}
|
}
|
||||||
if (perr == null) jsFreeValue(ctx, e);
|
if (perr == null) jsFreeValue(ctx, e);
|
||||||
return err;
|
return err;
|
||||||
@@ -409,6 +439,13 @@ Pointer jsGetPropertyValue(
|
|||||||
|
|
||||||
Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
||||||
if (val == null) return jsUNDEFINED();
|
if (val == null) return jsUNDEFINED();
|
||||||
|
if (val is JSError) {
|
||||||
|
final ret = jsNewError(ctx);
|
||||||
|
definePropertyValue(ctx, ret, "name", "");
|
||||||
|
definePropertyValue(ctx, ret, "message", val.message);
|
||||||
|
definePropertyValue(ctx, ret, "stack", val.stack);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
if (val is JSObject) return jsDupValue(ctx, val._val);
|
if (val is JSObject) return jsDupValue(ctx, val._val);
|
||||||
if (val is Future) {
|
if (val is Future) {
|
||||||
var resolvingFunc = allocate<Uint8>(count: sizeOfJSValue * 2);
|
var resolvingFunc = allocate<Uint8>(count: sizeOfJSValue * 2);
|
||||||
@@ -422,8 +459,8 @@ Pointer dartToJs(Pointer ctx, dynamic val, {Map<dynamic, dynamic> cache}) {
|
|||||||
free(resolvingFunc);
|
free(resolvingFunc);
|
||||||
val.then((value) {
|
val.then((value) {
|
||||||
res(value);
|
res(value);
|
||||||
}, onError: (e, stack) {
|
}, onError: (e) {
|
||||||
rej(e.toString() + '\n' + stack.toString());
|
rej(e);
|
||||||
});
|
});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -511,6 +548,15 @@ 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 (jsIsError(ctx, val) != 0) {
|
||||||
|
final err = jsToCString(ctx, val);
|
||||||
|
final pstack = jsGetPropertyValue(ctx, val, 'stack');
|
||||||
|
var stack;
|
||||||
|
if (jsToBool(ctx, pstack) != 0) {
|
||||||
|
stack = jsToCString(ctx, pstack);
|
||||||
|
}
|
||||||
|
jsFreeValue(ctx, pstack);
|
||||||
|
return JSError(err, stack);
|
||||||
} else if (jsIsPromise(ctx, val) != 0) {
|
} else if (jsIsPromise(ctx, val) != 0) {
|
||||||
Pointer jsPromiseThen = jsGetPropertyValue(ctx, val, 'then');
|
Pointer jsPromiseThen = jsGetPropertyValue(ctx, val, 'then');
|
||||||
JSFunction promiseThen = jsToDart(ctx, jsPromiseThen, cache: cache);
|
JSFunction promiseThen = jsToDart(ctx, jsPromiseThen, cache: cache);
|
||||||
@@ -521,11 +567,9 @@ dynamic jsToDart(Pointer ctx, Pointer val, {Map<int, dynamic> cache}) {
|
|||||||
(v) {
|
(v) {
|
||||||
if (!completer.isCompleted) completer.complete(v);
|
if (!completer.isCompleted) completer.complete(v);
|
||||||
},
|
},
|
||||||
NativeJSInvokable((ctx, thisVal, args) {
|
(e) {
|
||||||
if (!completer.isCompleted)
|
if (!completer.isCompleted) completer.completeError(e);
|
||||||
completer
|
},
|
||||||
.completeError(parseJSException(ctx, args[0]));
|
|
||||||
}),
|
|
||||||
], JSObject.fromAddress(ctx, val));
|
], JSObject.fromAddress(ctx, val));
|
||||||
bool isException = jsIsException(jsRet) != 0;
|
bool isException = jsIsException(jsRet) != 0;
|
||||||
jsFreeValue(ctx, jsRet);
|
jsFreeValue(ctx, jsRet);
|
||||||
|
@@ -12,6 +12,7 @@ import 'dart:io';
|
|||||||
import 'package:flutter_qjs/flutter_qjs.dart';
|
import 'package:flutter_qjs/flutter_qjs.dart';
|
||||||
import 'package:flutter_qjs/isolate.dart';
|
import 'package:flutter_qjs/isolate.dart';
|
||||||
import 'package:flutter_qjs/ffi.dart';
|
import 'package:flutter_qjs/ffi.dart';
|
||||||
|
import 'package:flutter_qjs/wrapper.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
dynamic myFunction(String args, {thisVal}) {
|
dynamic myFunction(String args, {thisVal}) {
|
||||||
@@ -30,6 +31,10 @@ Future testEvaluate(qjs) async {
|
|||||||
for (var i = 0; i < primities.length; i++) {
|
for (var i = 0; i < primities.length; i++) {
|
||||||
expect(wrapPrimities[i], primities[i], reason: "wrap primities");
|
expect(wrapPrimities[i], primities[i], reason: "wrap primities");
|
||||||
}
|
}
|
||||||
|
final jsError = JSError("test Error");
|
||||||
|
final wrapJsError = await testWrap(jsError);
|
||||||
|
expect(jsError.message, (wrapJsError as JSError).message,
|
||||||
|
reason: "wrap JSError");
|
||||||
final wrapFunction = await testWrap(testWrap);
|
final wrapFunction = await testWrap(testWrap);
|
||||||
final testEqual = await qjs.evaluate(
|
final testEqual = await qjs.evaluate(
|
||||||
"(a, b) => a === b",
|
"(a, b) => a === b",
|
||||||
@@ -60,8 +65,7 @@ Future testEvaluate(qjs) async {
|
|||||||
await promises[0];
|
await promises[0];
|
||||||
throw 'Future not reject';
|
throw 'Future not reject';
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e, startsWith('test Promise.reject\n'),
|
expect(e, 'test Promise.reject', reason: "promise object reject");
|
||||||
reason: "promise object reject");
|
|
||||||
}
|
}
|
||||||
expect(await promises[1], 'test Promise.resolve',
|
expect(await promises[1], 'test Promise.resolve',
|
||||||
reason: "promise object resolve");
|
reason: "promise object resolve");
|
||||||
@@ -156,9 +160,8 @@ void main() async {
|
|||||||
final qjs = FlutterQjs();
|
final qjs = FlutterQjs();
|
||||||
try {
|
try {
|
||||||
qjs.evaluate("a=()=>a();a();", name: "<eval>");
|
qjs.evaluate("a=()=>a();a();", name: "<eval>");
|
||||||
} catch (e) {
|
} on JSError catch (e) {
|
||||||
expect(
|
expect(e.message, 'InternalError: stack overflow',
|
||||||
e.toString(), startsWith('InternalError: stack overflow'),
|
|
||||||
reason: "throw stack overflow");
|
reason: "throw stack overflow");
|
||||||
}
|
}
|
||||||
qjs.close();
|
qjs.close();
|
||||||
|
Reference in New Issue
Block a user