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 : }
|