LCOV - code coverage report
Current view: top level - src - object.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 126 146 86.3 %
Date: 2021-03-31 22:28:15 Functions: 0 0 -

          Line data    Source code
       1             : /*
       2             :  * @Description: wrap object
       3             :  * @Author: ekibun
       4             :  * @Date: 2020-10-02 13:49:03
       5             :  * @LastEditors: ekibun
       6             :  * @LastEditTime: 2020-10-03 22:21:31
       7             :  */
       8             : part of '../flutter_qjs.dart';
       9             : 
      10             : /// js invokable
      11             : abstract class JSInvokable extends JSRef {
      12             :   dynamic invoke(List args, [dynamic thisVal]);
      13             : 
      14           3 :   static dynamic _wrap(dynamic func) {
      15           3 :     return func is JSInvokable
      16             :         ? func
      17           3 :         : func is Function
      18           3 :             ? _DartFunction(func)
      19             :             : func;
      20             :   }
      21             : }
      22             : 
      23             : class _DartFunction extends JSInvokable {
      24             :   final Function _func;
      25           3 :   _DartFunction(this._func);
      26             : 
      27           3 :   @override
      28             :   invoke(List args, [thisVal]) {
      29             :     /// wrap this into function
      30             :     final passThis =
      31          15 :         RegExp('{.*thisVal.*}').hasMatch(_func.runtimeType.toString());
      32             :     final ret =
      33           8 :         Function.apply(_func, args, passThis ? {#thisVal: thisVal} : null);
      34           3 :     JSRef.freeRecursive(args);
      35           3 :     JSRef.freeRecursive(thisVal);
      36             :     return ret;
      37             :   }
      38             : 
      39           0 :   @override
      40             :   String toString() {
      41           0 :     return _func.toString();
      42             :   }
      43             : 
      44           0 :   @override
      45             :   destroy() {}
      46             : }
      47             : 
      48             : /// implement this to capture js object release.
      49             : 
      50             : class _DartObject extends JSRef implements JSRefLeakable {
      51             :   Object? _obj;
      52             :   Pointer<JSContext>? _ctx;
      53           3 :   _DartObject(Pointer<JSContext> ctx, dynamic obj) {
      54           3 :     _ctx = ctx;
      55           3 :     _obj = obj;
      56           6 :     if (obj is JSRef) obj.dup();
      57          15 :     runtimeOpaques[jsGetRuntime(ctx)]?.addRef(this);
      58             :   }
      59             : 
      60           3 :   static _DartObject? fromAddress(Pointer<JSRuntime> rt, int val) {
      61          18 :     return runtimeOpaques[rt]?.getRef((e) => identityHashCode(e) == val)
      62             :         as _DartObject?;
      63             :   }
      64             : 
      65           0 :   @override
      66             :   String toString() {
      67           0 :     if (_ctx == null) return "DartObject(released)";
      68           0 :     return _obj.toString();
      69             :   }
      70             : 
      71           3 :   @override
      72             :   void destroy() {
      73           3 :     final ctx = _ctx;
      74           3 :     final obj = _obj;
      75           3 :     _ctx = null;
      76           3 :     _obj = null;
      77             :     if (ctx == null) return;
      78          15 :     runtimeOpaques[jsGetRuntime(ctx)]?.removeRef(this);
      79           6 :     if (obj is JSRef) obj.free();
      80             :   }
      81             : }
      82             : 
      83             : /// JS Error wrapper
      84             : class JSError extends _IsolateEncodable {
      85             :   late String message;
      86             :   late String stack;
      87           2 :   JSError(message, [stack]) {
      88           2 :     if (message is JSError) {
      89           0 :       this.message = message.message;
      90           0 :       this.stack = message.stack;
      91             :     } else {
      92           4 :       this.message = message.toString();
      93           5 :       this.stack = (stack ?? StackTrace.current).toString();
      94             :     }
      95             :   }
      96             : 
      97           0 :   @override
      98             :   String toString() {
      99           0 :     return stack.isEmpty ? message.toString() : "$message\n$stack";
     100             :   }
     101             : 
     102           3 :   static JSError? _decode(Map obj) {
     103           3 :     if (obj.containsKey(#jsError))
     104           6 :       return JSError(obj[#jsError], obj[#jsErrorStack]);
     105             :     return null;
     106             :   }
     107             : 
     108           2 :   @override
     109             :   Map _encode() {
     110           2 :     return {
     111           2 :       #jsError: message,
     112           2 :       #jsErrorStack: stack,
     113             :     };
     114             :   }
     115             : }
     116             : 
     117             : /// JS Object reference
     118             : /// call [release] to release js object.
     119             : class _JSObject extends JSRef {
     120             :   Pointer<JSValue>? _val;
     121             :   Pointer<JSContext>? _ctx;
     122             : 
     123             :   /// Create
     124           3 :   _JSObject(Pointer<JSContext> ctx, Pointer<JSValue> val) {
     125           3 :     this._ctx = ctx;
     126           6 :     final rt = jsGetRuntime(ctx);
     127           9 :     this._val = jsDupValue(ctx, val);
     128           9 :     runtimeOpaques[rt]?.addRef(this);
     129             :   }
     130             : 
     131           3 :   @override
     132             :   void destroy() {
     133           3 :     final ctx = _ctx;
     134           3 :     final val = _val;
     135           3 :     _val = null;
     136           3 :     _ctx = null;
     137             :     if (ctx == null || val == null) return;
     138           6 :     final rt = jsGetRuntime(ctx);
     139           9 :     runtimeOpaques[rt]?.removeRef(this);
     140           3 :     jsFreeValue(ctx, val);
     141             :   }
     142             : 
     143           1 :   @override
     144             :   String toString() {
     145           2 :     if (_ctx == null || _val == null) return "JSObject(released)";
     146           3 :     return jsToCString(_ctx!, _val!);
     147             :   }
     148             : }
     149             : 
     150             : /// JS function wrapper
     151             : class _JSFunction extends _JSObject implements JSInvokable, _IsolateEncodable {
     152           6 :   _JSFunction(Pointer<JSContext> ctx, Pointer<JSValue> val) : super(ctx, val);
     153             : 
     154           3 :   @override
     155             :   invoke(List<dynamic> arguments, [dynamic thisVal]) {
     156           3 :     final jsRet = _invoke(arguments, thisVal);
     157           3 :     final ctx = _ctx!;
     158           9 :     bool isException = jsIsException(jsRet) != 0;
     159             :     if (isException) {
     160           0 :       jsFreeValue(ctx, jsRet);
     161           0 :       throw _parseJSException(ctx);
     162             :     }
     163           3 :     final ret = _jsToDart(ctx, jsRet);
     164           3 :     jsFreeValue(ctx, jsRet);
     165             :     return ret;
     166             :   }
     167             : 
     168           3 :   Pointer<JSValue> _invoke(List<dynamic> arguments, [dynamic thisVal]) {
     169           3 :     final ctx = _ctx;
     170           3 :     final val = _val;
     171             :     if (ctx == null || val == null)
     172           0 :       throw JSError("InternalError: JSValue released");
     173             :     final args = arguments
     174           3 :         .map(
     175           6 :           (e) => _dartToJs(ctx, e),
     176             :         )
     177           3 :         .toList();
     178           3 :     final jsThis = _dartToJs(ctx, thisVal);
     179           3 :     final jsRet = jsCall(ctx, val, jsThis, args);
     180           3 :     jsFreeValue(ctx, jsThis);
     181           6 :     for (final jsArg in args) {
     182           3 :       jsFreeValue(ctx, jsArg);
     183             :     }
     184             :     return jsRet;
     185             :   }
     186             : 
     187           2 :   @override
     188             :   Map _encode() {
     189           4 :     return IsolateFunction._new(this)._encode();
     190             :   }
     191             : }
     192             : 
     193             : /// Dart function wrapper for isolate
     194             : class IsolateFunction extends JSInvokable implements _IsolateEncodable {
     195             :   int? _isolateId;
     196             :   SendPort? _port;
     197             :   JSInvokable? _invokable;
     198           3 :   IsolateFunction._fromId(this._isolateId, this._port);
     199             : 
     200           3 :   IsolateFunction._new(this._invokable) {
     201           6 :     _handlers.add(this);
     202             :   }
     203           3 :   IsolateFunction(Function func) : this._new(_DartFunction(func));
     204             : 
     205             :   static ReceivePort? _invokeHandler;
     206           6 :   static Set<IsolateFunction> _handlers = Set();
     207             : 
     208           3 :   static get _handlePort {
     209             :     if (_invokeHandler == null) {
     210           3 :       _invokeHandler = ReceivePort();
     211           6 :       _invokeHandler!.listen((msg) async {
     212           3 :         final msgPort = msg[#port];
     213             :         try {
     214           6 :           final handler = _handlers.firstWhereOrNull(
     215          12 :             (v) => identityHashCode(v) == msg[#handler],
     216             :           );
     217           0 :           if (handler == null) throw JSError('handler released');
     218          12 :           final ret = _encodeData(await handler._handle(msg[#msg]));
     219           3 :           if (msgPort != null) msgPort.send(ret);
     220             :         } catch (e) {
     221           0 :           final err = _encodeData(e);
     222             :           if (msgPort != null)
     223           0 :             msgPort.send({
     224             :               #error: err,
     225             :             });
     226             :         }
     227             :       });
     228             :     }
     229           3 :     return _invokeHandler!.sendPort;
     230             :   }
     231             : 
     232           3 :   _send(msg) async {
     233           3 :     final port = _port;
     234           1 :     if (port == null) return _handle(msg);
     235           3 :     final evaluatePort = ReceivePort();
     236           6 :     port.send({
     237           3 :       #handler: _isolateId,
     238             :       #msg: msg,
     239           3 :       #port: evaluatePort.sendPort,
     240             :     });
     241           6 :     final result = await evaluatePort.first;
     242           5 :     if (result is Map && result.containsKey(#error))
     243           0 :       throw _decodeData(result[#error]);
     244           3 :     return _decodeData(result);
     245             :   }
     246             : 
     247           2 :   _destroy() {
     248           4 :     _handlers.remove(this);
     249           4 :     _invokable?.free();
     250           2 :     _invokable = null;
     251             :   }
     252             : 
     253           3 :   _handle(msg) async {
     254             :     switch (msg) {
     255           3 :       case #dup:
     256           6 :         _refCount++;
     257             :         return null;
     258           3 :       case #free:
     259           6 :         _refCount--;
     260           8 :         if (_refCount < 0) _destroy();
     261             :         return null;
     262           3 :       case #destroy:
     263           0 :         _destroy();
     264             :         return null;
     265             :     }
     266           6 :     final List args = _decodeData(msg[#args]);
     267           6 :     final thisVal = _decodeData(msg[#thisVal]);
     268           6 :     return _invokable?.invoke(args, thisVal);
     269             :   }
     270             : 
     271             :   @override
     272           3 :   Future invoke(List positionalArguments, [thisVal]) async {
     273           3 :     final List dArgs = _encodeData(positionalArguments);
     274           3 :     final dThisVal = _encodeData(thisVal);
     275           6 :     return _send({
     276             :       #args: dArgs,
     277             :       #thisVal: dThisVal,
     278             :     });
     279             :   }
     280             : 
     281           3 :   static IsolateFunction? _decode(Map obj) {
     282           3 :     if (obj.containsKey(#jsFunctionPort))
     283           3 :       return IsolateFunction._fromId(
     284           3 :         obj[#jsFunctionId],
     285           3 :         obj[#jsFunctionPort],
     286             :       );
     287             :     return null;
     288             :   }
     289             : 
     290           3 :   @override
     291             :   Map _encode() {
     292           3 :     return {
     293           6 :       #jsFunctionId: _isolateId ?? identityHashCode(this),
     294           6 :       #jsFunctionPort: _port ?? IsolateFunction._handlePort,
     295             :     };
     296             :   }
     297             : 
     298             :   int _refCount = 0;
     299             : 
     300           3 :   @override
     301             :   dup() {
     302           3 :     _send(#dup);
     303             :   }
     304             : 
     305           3 :   @override
     306             :   free() {
     307           3 :     _send(#free);
     308             :   }
     309             : 
     310           0 :   @override
     311             :   void destroy() {
     312           0 :     _send(#destroy);
     313             :   }
     314             : }

Generated by: LCOV version 1.14