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

          Line data    Source code
       1             : /*
       2             :  * @Description: quickjs engine
       3             :  * @Author: ekibun
       4             :  * @Date: 2020-08-08 08:29:09
       5             :  * @LastEditors: ekibun
       6             :  * @LastEditTime: 2020-10-06 23:47:13
       7             :  */
       8             : part of '../flutter_qjs.dart';
       9             : 
      10             : /// Handler function to manage js module.
      11             : typedef _JsModuleHandler = String Function(String name);
      12             : 
      13             : /// Handler to manage unhandled promise rejection.
      14             : typedef _JsHostPromiseRejectionHandler = void Function(dynamic reason);
      15             : 
      16             : /// Quickjs engine for flutter.
      17             : class FlutterQjs {
      18             :   Pointer<JSRuntime>? _rt;
      19             :   Pointer<JSContext>? _ctx;
      20             : 
      21             :   /// Max stack size for quickjs.
      22             :   final int? stackSize;
      23             : 
      24             :   /// Message Port for event loop. Close it to stop dispatching event loop.
      25             :   ReceivePort port = ReceivePort();
      26             : 
      27             :   /// Handler function to manage js module.
      28             :   final _JsModuleHandler? moduleHandler;
      29             : 
      30             :   /// Handler function to manage js module.
      31             :   final _JsHostPromiseRejectionHandler? hostPromiseRejectionHandler;
      32             : 
      33           3 :   FlutterQjs({
      34             :     this.moduleHandler,
      35             :     this.stackSize,
      36             :     this.hostPromiseRejectionHandler,
      37             :   });
      38             : 
      39           3 :   _ensureEngine() {
      40           3 :     if (_rt != null) return;
      41           6 :     final rt = jsNewRuntime((ctx, type, ptr) {
      42             :       try {
      43             :         switch (type) {
      44           3 :           case JSChannelType.METHON:
      45           3 :             final pdata = ptr.cast<Pointer<JSValue>>();
      46           9 :             final argc = pdata.elementAt(1).value.cast<Int32>().value;
      47           3 :             final pargs = [];
      48           6 :             for (var i = 0; i < argc; ++i) {
      49           6 :               pargs.add(_jsToDart(
      50             :                 ctx,
      51           3 :                 Pointer.fromAddress(
      52          15 :                   pdata.elementAt(2).value.address + sizeOfJSValue * i,
      53             :                 ),
      54             :               ));
      55             :             }
      56           3 :             final JSInvokable func = _jsToDart(
      57             :               ctx,
      58           3 :               pdata.elementAt(3).value,
      59             :             );
      60           3 :             return _dartToJs(
      61             :                 ctx,
      62           3 :                 func.invoke(
      63             :                   pargs,
      64           6 :                   _jsToDart(ctx, pdata.elementAt(0).value),
      65             :                 ));
      66           3 :           case JSChannelType.MODULE:
      67           1 :             if (moduleHandler == null) throw JSError('No ModuleHandler');
      68           2 :             final ret = moduleHandler!(
      69           2 :               ptr.cast<Utf8>().toDartString(),
      70           1 :             ).toNativeUtf8();
      71           2 :             Future.microtask(() {
      72           1 :               malloc.free(ret);
      73             :             });
      74           1 :             return ret.cast();
      75           3 :           case JSChannelType.PROMISE_TRACK:
      76           2 :             final err = _parseJSException(ctx, ptr);
      77           2 :             if (hostPromiseRejectionHandler != null) {
      78           4 :               hostPromiseRejectionHandler!(err);
      79             :             } else {
      80           0 :               print('unhandled promise rejection: $err');
      81             :             }
      82           2 :             return nullptr;
      83           3 :           case JSChannelType.FREE_OBJECT:
      84           3 :             final rt = ctx.cast<JSRuntime>();
      85           9 :             _DartObject.fromAddress(rt, ptr.address)?.free();
      86           3 :             return nullptr;
      87             :         }
      88           0 :         throw JSError('call channel with wrong type');
      89             :       } catch (e) {
      90           0 :         if (type == JSChannelType.FREE_OBJECT) {
      91           0 :           print('DartObject release error: $e');
      92           0 :           return nullptr;
      93             :         }
      94           0 :         if (type == JSChannelType.MODULE) {
      95           0 :           print('host Promise Rejection Handler error: $e');
      96           0 :           return nullptr;
      97             :         }
      98           0 :         final throwObj = _dartToJs(ctx, e);
      99           0 :         final err = jsThrow(ctx, throwObj);
     100           0 :         jsFreeValue(ctx, throwObj);
     101           0 :         if (type == JSChannelType.MODULE) {
     102           0 :           jsFreeValue(ctx, err);
     103           0 :           return nullptr;
     104             :         }
     105             :         return err;
     106             :       }
     107           3 :     }, port);
     108           3 :     final stackSize = this.stackSize ?? 0;
     109           3 :     if (stackSize > 0) jsSetMaxStackSize(rt, stackSize);
     110           3 :     _rt = rt;
     111           6 :     _ctx = jsNewContext(rt);
     112             :   }
     113             : 
     114             :   /// Free Runtime and Context which can be recreate when evaluate again.
     115           3 :   close() {
     116           3 :     final rt = _rt;
     117           3 :     final ctx = _ctx;
     118           3 :     _rt = null;
     119           3 :     _ctx = null;
     120           6 :     if (ctx != null) jsFreeContext(ctx);
     121             :     if (rt == null) return;
     122           3 :     _executePendingJob();
     123             :     try {
     124           3 :       jsFreeRuntime(rt);
     125           1 :     } on String catch (e) {
     126           1 :       throw JSError(e);
     127             :     }
     128             :   }
     129             : 
     130           3 :   void _executePendingJob() {
     131           3 :     final rt = _rt;
     132           3 :     final ctx = _ctx;
     133             :     if (rt == null || ctx == null) return;
     134             :     while (true) {
     135           6 :       int err = jsExecutePendingJob(rt);
     136           3 :       if (err <= 0) {
     137           3 :         if (err < 0) print(_parseJSException(ctx));
     138             :         break;
     139             :       }
     140             :     }
     141             :   }
     142             : 
     143             :   /// Dispatch JavaScript Event loop.
     144           3 :   Future<void> dispatch() async {
     145           9 :     await for (final _ in port) {
     146           3 :       _executePendingJob();
     147             :     }
     148             :   }
     149             : 
     150             :   /// Evaluate js script.
     151           3 :   dynamic evaluate(
     152             :     String command, {
     153             :     String? name,
     154             :     int? evalFlags,
     155             :   }) {
     156           3 :     _ensureEngine();
     157           3 :     final ctx = _ctx!;
     158           3 :     final jsval = jsEval(
     159             :       ctx,
     160             :       command,
     161             :       name ?? '<eval>',
     162             :       evalFlags ?? JSEvalFlag.GLOBAL,
     163             :     );
     164           9 :     if (jsIsException(jsval) != 0) {
     165           1 :       jsFreeValue(ctx, jsval);
     166           1 :       throw _parseJSException(ctx);
     167             :     }
     168           3 :     final result = _jsToDart(ctx, jsval);
     169           3 :     jsFreeValue(ctx, jsval);
     170             :     return result;
     171             :   }
     172             : }

Generated by: LCOV version 1.14