• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #pragma once
17 
18 #include <node.h>
19 #include <napi.h>
20 #include <assert.h>
21 
22 #include "common-interop.h"
23 
24 #ifdef SK_BUILD_FOR_WIN
25 #include <windows.h>
26 #else
27 #include <time.h>
28 #endif
29 
V8LocalValueFromJsValue(napi_value v)30 static inline v8::Local<v8::Value> V8LocalValueFromJsValue(napi_value v) {
31   v8::Local<v8::Value> local;
32   memcpy_s(static_cast<void*>(&local), sizeof(local), &v, sizeof(v));
33   return local;
34 }
35 
36 struct RedrawerPeer;
37 
38 typedef RedrawerPeer* (*peerFactory_t)(void* arg, v8::Local<v8::Object> redrawer);
39 
40 struct NodeScopedState {
41     v8::Isolate* isolate;
42 
43     std::unique_ptr<v8::Locker> locker;
44     std::unique_ptr<v8::Isolate::Scope> isolate_scope;
45     std::unique_ptr<v8::Context::Scope> context_scope;
46 
47     v8::Global<v8::Context> context;
48 
49     v8::Global<v8::Function> onProvidePlatformData;
50     v8::Global<v8::Function> onInit;
51     v8::Global<v8::Function> onDeinit;
52     v8::Global<v8::Function> onFrameEvent;
53     v8::Global<v8::Function> onRequestRedraw;
54     v8::Global<v8::Function> onTap;
55     v8::Global<v8::Function> onMove;
56     v8::Global<v8::Function> onMultiTouchTap;
57     v8::Global<v8::Function> onMultiTouchMove;
58     v8::Global<v8::Function> onFrameMove;
59     v8::Global<v8::Function> onKey;
60     v8::Global<v8::Function> onTextInput;
61     v8::Global<v8::Function> onScroll;
62     v8::Global<v8::Function> onResize;
63     v8::Global<v8::Function> onDrop;
64     v8::Global<v8::Function> permissionGranted;
65 
66     bool callbacksStopped;
67 
NodeScopedStateNodeScopedState68     NodeScopedState(v8::Isolate* isolate, v8::Local<v8::Context> context)
69             : isolate(isolate), callbacksStopped(true) {
70         this->context = v8::Global<v8::Context>(isolate, context);
71     }
72 
NodeScopedStateNodeScopedState73     NodeScopedState(node::CommonEnvironmentSetup* setup)
74         : isolate(setup->isolate()), callbacksStopped(true) {
75         locker = std::make_unique<v8::Locker>(isolate);
76         v8::HandleScope handleScope(isolate);
77         isolate_scope = std::make_unique<v8::Isolate::Scope>(isolate);
78         v8::Local<v8::Context> context = setup->context();
79         this->context = v8::Global<v8::Context>(isolate, context);
80         context_scope = std::make_unique<v8::Context::Scope>(context);
81     }
82 
~NodeScopedStateNodeScopedState83     ~NodeScopedState() {
84         // TODO: fix me properly.
85         context_scope.release();
86         isolate_scope.release();
87     }
88 
initPlatformNodeScopedState89     void initPlatform(v8::Local<v8::Object> api) {
90         v8::Local<v8::Context> context = this->context.Get(isolate);
91         v8::Local<v8::Value> providePlatformData = api->Get(context, makeString("providePlatformData")).ToLocalChecked();
92         if (providePlatformData->IsFunction()) {
93             this->onProvidePlatformData = v8::Global<v8::Function>(isolate, providePlatformData.As<v8::Function>());
94         }
95         v8::Local<v8::Value> onTap = api->Get(context, makeString("onTap")).ToLocalChecked();
96         if (onTap->IsFunction()) {
97             this->onTap = v8::Global<v8::Function>(isolate, onTap.As<v8::Function>());
98         }
99         v8::Local<v8::Value> onMove = api->Get(context, makeString("onMove")).ToLocalChecked();
100         if (onMove->IsFunction()) {
101             this->onMove = v8::Global<v8::Function>(isolate, onMove.As<v8::Function>());
102         }
103         v8::Local<v8::Value> onMultiTouchTap = api->Get(context, makeString("onMultiTouchTap")).ToLocalChecked();
104         if (onMultiTouchTap->IsFunction()) {
105             this->onMultiTouchTap = v8::Global<v8::Function>(isolate, onMultiTouchTap.As<v8::Function>());
106         }
107         v8::Local<v8::Value> onMultiTouchMove = api->Get(context, makeString("onMultiTouchMove")).ToLocalChecked();
108         if (onMultiTouchMove->IsFunction()) {
109             this->onMultiTouchMove = v8::Global<v8::Function>(isolate, onMultiTouchMove.As<v8::Function>());
110         }
111         v8::Local<v8::Value> onKey = api->Get(context, makeString("onKey")).ToLocalChecked();
112         if (onKey->IsFunction()) {
113             this->onKey = v8::Global<v8::Function>(isolate, onKey.As<v8::Function>());
114         }
115         v8::Local<v8::Value> permissionGranted = api->Get(context, makeString("permissionGranted")).ToLocalChecked();
116         if (permissionGranted->IsFunction()) {
117             this->permissionGranted = v8::Global<v8::Function>(isolate, permissionGranted.As<v8::Function>());
118         }
119         v8::Local<v8::Value> onTextInput = api->Get(context, makeString("onTextInput")).ToLocalChecked();
120         if (onTextInput->IsFunction()) {
121             this->onTextInput = v8::Global<v8::Function>(isolate, onTextInput.As<v8::Function>());
122         }
123         v8::Local<v8::Value> onScroll = api->Get(context, makeString("onScroll")).ToLocalChecked();
124         if (onScroll->IsFunction()) {
125             this->onScroll = v8::Global<v8::Function>(isolate, onScroll.As<v8::Function>());
126         }
127         v8::Local<v8::Value> onResize = api->Get(context, makeString("onResize")).ToLocalChecked();
128         if (onResize->IsFunction()) {
129             this->onResize = v8::Global<v8::Function>(isolate, onResize.As<v8::Function>());
130         }
131         v8::Local<v8::Value> onDrop = api->Get(context, makeString("onDrop")).ToLocalChecked();
132         if (onDrop->IsFunction()) {
133             this->onDrop = v8::Global<v8::Function>(isolate, onDrop.As<v8::Function>());
134         }
135         v8::Local<v8::Value> onFrameMove = api->Get(context, makeString("onFrameMove")).ToLocalChecked();
136         if (onFrameMove->IsFunction()) {
137             this->onFrameMove = v8::Global<v8::Function>(isolate, onFrameMove.As<v8::Function>());
138         }
139         v8::Local<v8::Value> onInit = api->Get(context, makeString("onInit")).ToLocalChecked();
140         if (onInit->IsFunction()) {
141             this->onInit = v8::Global<v8::Function>(isolate, onInit.As<v8::Function>());
142         }
143         v8::Local<v8::Value> onDeinit = api->Get(context, makeString("onDeinit")).ToLocalChecked();
144         if (onDeinit->IsFunction()) {
145             this->onDeinit = v8::Global<v8::Function>(isolate, onDeinit.As<v8::Function>());
146         }
147         v8::Local<v8::Value> onFrameEvent = api->Get(context, makeString("onFrameEvent")).ToLocalChecked();
148         if (onFrameEvent->IsFunction()) {
149             this->onFrameEvent = v8::Global<v8::Function>(isolate, onFrameEvent.As<v8::Function>());
150         }
151         v8::Local<v8::Value> onRequestRedraw = api->Get(context, makeString("onRequestRedraw")).ToLocalChecked();
152         if (onRequestRedraw->IsFunction()) {
153             this->onRequestRedraw = v8::Global<v8::Function>(isolate, onRequestRedraw.As<v8::Function>());
154         }
155     }
156 
makePointerAsLocalNodeScopedState157     v8::Local<v8::Value> makePointerAsLocal(void* ptr) {
158         // Keep in sync with NAPI version.
159         v8::Local<v8::BigInt> result = v8::BigInt::NewFromUnsigned(isolate, (uint64_t) ptr);
160         return result;
161     }
162 
providePlatformDataNodeScopedState163     void providePlatformData(peerFactory_t factory, void* ctx) {
164         if (onProvidePlatformData.IsEmpty()) return;
165         v8::Local<v8::Value> argv[2] = {
166             makePointerAsLocal(reinterpret_cast<void*>(factory)),
167             makePointerAsLocal(reinterpret_cast<void*>(ctx))
168         };
169         v8::Local<v8::Function> providePlatformData = this->onProvidePlatformData.Get(isolate);
170         auto result = providePlatformData->Call(isolate->GetCurrentContext(), v8::Object::New(isolate), 2, argv);
171         (void)result;
172     }
173 
callPermissionGrantedNodeScopedState174     void callPermissionGranted(
175         v8::Local<v8::Object> redrawer,
176         int requestCode,
177         std::vector<std::string>& permissions,
178         std::vector<int>& grantResults) {
179         if (permissionGranted.IsEmpty() || callbacksStopped) return;
180 
181         v8::Local<v8::Function> permissionGranted = this->permissionGranted.Get(isolate);
182 
183         int permissionsNumber = permissions.size();
184         v8::Local<v8::Value>* permissionNames = new v8::Local<v8::Value>[permissionsNumber];
185         v8::Local<v8::Value>* permissionGrants = new v8::Local<v8::Value>[permissionsNumber];
186 
187         for (int i=0; i<permissionsNumber; i++) {
188             permissionNames[i] = v8::String::NewFromUtf8(isolate, permissions[i].c_str()).ToLocalChecked();
189             permissionGrants[i] = v8::Number::New(isolate, grantResults[i]);
190         }
191 
192         v8::Local<v8::Array> grantsValue = v8::Array::New(isolate, permissionGrants, permissionsNumber);
193         v8::Local<v8::Array> namesValue = v8::Array::New(isolate, permissionNames, permissionsNumber);
194 
195         v8::Local<v8::Value> argv[3] = {
196             v8::Number::New(isolate, requestCode),
197             namesValue,
198             grantsValue
199         };
200 
201         auto result = permissionGranted->Call(isolate->GetCurrentContext(), redrawer, 3, argv);
202         (void)result;
203     }
204 
callOnInitNodeScopedState205     void callOnInit(v8::Local<v8::Object> redrawer, void* peer) {
206         if (onInit.IsEmpty()) return;
207         v8::Local<v8::Function> onInit = this->onInit.Get(isolate);
208         v8::Local<v8::Value> argv[1] = {
209             makePointerAsLocal(peer)
210         };
211         auto result = onInit->Call(isolate->GetCurrentContext(), redrawer, 1, argv);
212         (void)result;
213     }
214 
callOnDeinitNodeScopedState215     void callOnDeinit(v8::Local<v8::Object> redrawer) {
216         if (onDeinit.IsEmpty()) return;
217         v8::Local<v8::Function> onDeinit = this->onDeinit.Get(isolate);
218         auto result = onDeinit->Call(isolate->GetCurrentContext(), redrawer, 0, nullptr);
219         (void)result;
220     }
221 
callOnFrameEventNodeScopedState222     void callOnFrameEvent(v8::Local<v8::Object> redrawer, FrameEventType type) {
223         if (onFrameEvent.IsEmpty()) return;
224         v8::Local<v8::Function> onFrameEvent = this->onFrameEvent.Get(isolate);
225         v8::Local<v8::Value> argv[1] = {
226                 v8::Number::New(isolate, (int)type)
227         };
228         auto result = onFrameEvent->Call(isolate->GetCurrentContext(), redrawer, 1, argv);
229         (void)result;
230     }
231 
callOnRequestRedrawNodeScopedState232     void callOnRequestRedraw(v8::Local<v8::Object> redrawer) {
233         if (onRequestRedraw.IsEmpty()) return;
234         v8::Local<v8::Function> onRequestRedraw = this->onRequestRedraw.Get(isolate);
235         auto result = onRequestRedraw->Call(isolate->GetCurrentContext(), redrawer, 0, nullptr);
236         (void)result;
237     }
238 
makeStringNodeScopedState239     v8::Local<v8::Value> makeString(const char* str) {
240         return v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(str)).ToLocalChecked();
241     }
242 
callOnTapNodeScopedState243     void callOnTap(v8::Local<v8::Object> redrawer, int count, int* xValues, int* yValues, int tap, int target, int button) {
244         if (count > 1) {
245             if (onMultiTouchTap.IsEmpty() || callbacksStopped) return;
246             v8::Local<v8::Value>* xV8Values = new v8::Local<v8::Value>[count];
247             v8::Local<v8::Value>* yV8Values = new v8::Local<v8::Value>[count];
248 
249             for (int i = 0; i < count; i++) {
250                 xV8Values[i] = v8::Number::New(isolate, xValues[i]);
251                 yV8Values[i] = v8::Number::New(isolate, yValues[i]);
252             }
253 
254             v8::Local<v8::Function> onMultiTouchTap = this->onMultiTouchTap.Get(isolate);
255             v8::Local<v8::Value> argv[4] = {
256                     v8::Array::New(isolate, xV8Values, count),
257                     v8::Array::New(isolate, yV8Values, count),
258                     v8::Number::New(isolate, tap),
259                     v8::Number::New(isolate, target)
260             };
261             auto result = onMultiTouchTap->Call(isolate->GetCurrentContext(), redrawer, 4, argv);
262             (void)result;
263         } else {
264             if (onMove.IsEmpty() || callbacksStopped) return;
265             v8::Local<v8::Function> onTap = this->onTap.Get(isolate);
266             v8::Local<v8::Value> argv[4] = {
267                     v8::Number::New(isolate, xValues[0]),
268                     v8::Number::New(isolate, yValues[0]),
269                     v8::Number::New(isolate, tap),
270                     v8::Number::New(isolate, button)
271             };
272             auto result = onTap->Call(isolate->GetCurrentContext(), redrawer, 4, argv);
273             (void)result;
274         }
275     }
276 
callOnMoveNodeScopedState277     void callOnMove(v8::Local<v8::Object> redrawer, int count, int* xValues, int* yValues) {
278         if (count > 1) {
279             if (onMultiTouchMove.IsEmpty() || callbacksStopped) return;
280             v8::Local<v8::Value>* xV8Values = new v8::Local<v8::Value>[count];
281             v8::Local<v8::Value>* yV8Values = new v8::Local<v8::Value>[count];
282 
283             for (int i = 0; i < count; i++) {
284                 xV8Values[i] = v8::Number::New(isolate, xValues[i]);
285                 yV8Values[i] = v8::Number::New(isolate, yValues[i]);
286             }
287 
288             v8::Local<v8::Function> onMultiTouchMove = this->onMultiTouchMove.Get(isolate);
289             v8::Local<v8::Value> argv[2] = {
290                     v8::Array::New(isolate, xV8Values, count),
291                     v8::Array::New(isolate, yV8Values, count)
292             };
293             auto result = onMultiTouchMove->Call(isolate->GetCurrentContext(), redrawer, 2, argv);
294             (void)result;
295         } else {
296             if (onMove.IsEmpty() || callbacksStopped) return;
297             v8::Local<v8::Function> onMove = this->onMove.Get(isolate);
298             v8::Local<v8::Value> argv[2] = {
299                     v8::Number::New(isolate, xValues[0]),
300                     v8::Number::New(isolate, yValues[0])
301             };
302             auto result = onMove->Call(isolate->GetCurrentContext(), redrawer, 2, argv);
303             (void)result;
304         }
305     }
306 
callOnKeyNodeScopedState307     void callOnKey(v8::Local<v8::Object> redrawer, int key, int modifiers, int isPressed) {
308         if (onKey.IsEmpty() || callbacksStopped) return;
309 
310         v8::Local<v8::Function> onKey = this->onKey.Get(isolate);
311         v8::Local<v8::Value> argv[3] = {
312                 v8::Number::New(isolate, key),
313                 v8::Number::New(isolate, modifiers),
314                 v8::Number::New(isolate, isPressed)
315         };
316         auto result = onKey->Call(isolate->GetCurrentContext(), redrawer, 3, argv);
317         (void)result;
318     }
319 
callOnTextInputNodeScopedState320     void callOnTextInput(v8::Local<v8::Object> redrawer, int type, int cursorPosition, std::string characters) {
321         if (onTextInput.IsEmpty() || callbacksStopped) return;
322 
323         v8::Local<v8::Function> onTextInput = this->onTextInput.Get(isolate);
324         v8::Local<v8::Value> argv[3] = {
325                 v8::Number::New(isolate, type),
326                 v8::Number::New(isolate, cursorPosition),
327                 v8::String::NewFromUtf8(isolate, characters.c_str(), v8::NewStringType::kNormal, characters.size()).ToLocalChecked()
328         };
329         auto result = onTextInput->Call(isolate->GetCurrentContext(), redrawer, 3, argv);
330         (void)result;
331     }
332 
callOnScrollNodeScopedState333     void callOnScroll(v8::Local<v8::Object> redrawer, int xpos, int ypos, int xoffset, int yoffset) {
334         if (onScroll.IsEmpty() || callbacksStopped) return;
335         v8::Local<v8::Function> onScroll = this->onScroll.Get(isolate);
336         v8::Local<v8::Value> argv[4] = {
337                 v8::Number::New(isolate, xpos),
338                 v8::Number::New(isolate, ypos),
339                 v8::Number::New(isolate, xoffset),
340                 v8::Number::New(isolate, yoffset)
341         };
342         auto result = onScroll->Call(isolate->GetCurrentContext(), redrawer, 4, argv);
343         (void)result;
344     }
345 
callOnResizeNodeScopedState346     void callOnResize(v8::Local<v8::Object> redrawer, int width, int height) {
347         if (onResize.IsEmpty() || callbacksStopped) return;
348         v8::Local<v8::Function> onResize = this->onResize.Get(isolate);
349         v8::Local<v8::Value> argv[2] = {
350                 v8::Number::New(isolate, width),
351                 v8::Number::New(isolate, height)
352         };
353         auto result = onResize->Call(isolate->GetCurrentContext(), redrawer, 2, argv);
354         (void)result;
355     }
356 
callOnDropNodeScopedState357     void callOnDrop(v8::Local<v8::Object> redrawer, int count, const char** paths, int x, int y) {
358         if (onDrop.IsEmpty() || callbacksStopped) return;
359         v8::Local<v8::Function> onDrop = this->onDrop.Get(isolate);
360         v8::Local<v8::Value>* pathsV8 = new v8::Local<v8::Value>[count];
361 
362         for (int i = 0; i < count; i++) {
363             pathsV8[i] = v8::String::NewFromUtf8(isolate, paths[i]).ToLocalChecked();
364         }
365         v8::Local<v8::Value> argv[3] = {
366             v8::Array::New(isolate, pathsV8, count),
367             v8::Number::New(isolate, x),
368             v8::Number::New(isolate, y),
369         };
370 
371         auto result = onDrop->Call(isolate->GetCurrentContext(), redrawer, 3, argv);
372         (void)result;
373     }
374 
callOnFrameMoveNodeScopedState375     void callOnFrameMove(v8::Local<v8::Object> redrawer, int x, int y) {
376         if (onFrameMove.IsEmpty() || callbacksStopped) return;
377         v8::Local<v8::Function> onFrameMove = this->onFrameMove.Get(isolate);
378         v8::Local<v8::Value> argv[2] = {
379                 v8::Number::New(isolate, x),
380                 v8::Number::New(isolate, y)
381         };
382         auto result = onFrameMove->Call(isolate->GetCurrentContext(), redrawer, 2, argv);
383         (void)result;
384     }
385 
startCallbacksNodeScopedState386     void startCallbacks() {
387         callbacksStopped = false;
388     }
389 
stopCallbacksNodeScopedState390     void stopCallbacks() {
391         callbacksStopped = true;
392     }
393 };
394 
395 struct RedrawerPeer : public RedrawerPeerBase {
396     NodeScopedState* node;
397     v8::Global<v8::Object> redrawer;
RedrawerPeerRedrawerPeer398     RedrawerPeer(NodeScopedState* node, v8::Local<v8::Object> redrawer)
399         : node(node), redrawer(v8::Global<v8::Object>(node->isolate, redrawer))
400         {}
401 
callRequestRedrawRedrawerPeer402     void callRequestRedraw() {
403         v8::HandleScope scope(node->isolate);
404         if (redrawer.IsEmpty()) return;
405         node->callOnRequestRedraw(this->redrawer.Get(node->isolate));
406     }
407 
callOnInitRedrawerPeer408     void callOnInit() {
409         v8::HandleScope scope(node->isolate);
410         if (redrawer.IsEmpty()) return;
411         node->callOnInit(this->redrawer.Get(node->isolate), this);
412     }
413 
callOnDeinitRedrawerPeer414     void callOnDeinit() {
415         v8::HandleScope scope(node->isolate);
416         if (redrawer.IsEmpty()) return;
417         node->callOnDeinit(this->redrawer.Get(node->isolate));
418     }
419 
callOnFrameEventRedrawerPeer420     void callOnFrameEvent(FrameEventType type) {
421         v8::HandleScope scope(node->isolate);
422         if (redrawer.IsEmpty()) return;
423         node->callOnFrameEvent(this->redrawer.Get(node->isolate), type);
424     }
425 
callOnTapRedrawerPeer426     void callOnTap(int count, int* xValues, int* yValues, int tap, int target, int button) {
427         v8::HandleScope scope(node->isolate);
428         if (redrawer.IsEmpty()) return;
429         node->callOnTap(this->redrawer.Get(node->isolate), count, xValues, yValues, tap, target, button);
430     }
431 
callOnMoveRedrawerPeer432     void callOnMove(int count, int* xValues, int* yValues) {
433         v8::HandleScope scope(node->isolate);
434         if (redrawer.IsEmpty()) return;
435         node->callOnMove(this->redrawer.Get(node->isolate), count, xValues, yValues);
436     }
437 
callOnKeyRedrawerPeer438     void callOnKey(int key, int modifiers, int isPressed) {
439         v8::HandleScope scope(node->isolate);
440         if (redrawer.IsEmpty()) return;
441         node->callOnKey(this->redrawer.Get(node->isolate), key, modifiers, isPressed);
442     }
443 
callPermissionGrantedRedrawerPeer444     void callPermissionGranted(int requestCode,
445         std::vector<std::string>& permissions,
446         std::vector<int>& grantResults) {
447         v8::HandleScope scope(node->isolate);
448         if (redrawer.IsEmpty()) return;
449 
450         node->callPermissionGranted(
451             this->redrawer.Get(node->isolate),
452             requestCode,
453             permissions,
454             grantResults);
455     }
456 
callOnTextInputRedrawerPeer457     void callOnTextInput(int type, int cursorPosition, std::string characters) {
458         v8::HandleScope scope(node->isolate);
459         if (redrawer.IsEmpty()) return;
460         node->callOnTextInput(this->redrawer.Get(node->isolate), type, cursorPosition, characters);
461     }
462 
callOnScrollRedrawerPeer463     void callOnScroll(int xpos, int ypos, int xoffset, int yoffset) {
464         v8::HandleScope scope(node->isolate);
465         if (redrawer.IsEmpty()) return;
466         node->callOnScroll(this->redrawer.Get(node->isolate), xpos, ypos, xoffset, yoffset);
467     }
468 
callOnResizeRedrawerPeer469     void callOnResize(int width, int height) {
470         v8::HandleScope scope(node->isolate);
471         if (redrawer.IsEmpty()) return;
472         node->callOnResize(this->redrawer.Get(node->isolate), width, height);
473     }
474 
callOnDropRedrawerPeer475     void callOnDrop(int count, const char** paths, int x, int y) {
476         v8::HandleScope scope(node->isolate);
477         if (redrawer.IsEmpty()) return;
478         node->callOnDrop(this->redrawer.Get(node->isolate), count, paths, x, y);
479     }
480 
callOnFrameMoveRedrawerPeer481     void callOnFrameMove(int x, int y) {
482         v8::HandleScope scope(node->isolate);
483         if (redrawer.IsEmpty()) return;
484         node->callOnFrameMove(this->redrawer.Get(node->isolate), x, y);
485     }
486 };
487 
488 struct NodeCallback {
489     v8::Isolate* isolate;
490     v8::Global<v8::Context> context;
491     v8::Global<v8::Object> receiver;
492     v8::Global<v8::Function> callback;
493 
NodeCallbackNodeCallback494     NodeCallback(v8::Isolate* isolate, v8::Local<v8::Context> context,
495         v8::Local<v8::Object> receiver, v8::Local<v8::Function> callback)
496         : isolate(isolate),
497           context(v8::Global<v8::Context>(isolate, context)),
498           receiver(v8::Global<v8::Object>(isolate, receiver)),
499           callback(v8::Global<v8::Function>(isolate, callback))
500         {}
501 
callCallbackNodeCallback502     v8::Local<v8::Value> callCallback() {
503         if (callback.IsEmpty()) {
504             return v8::Undefined(isolate);
505         }
506         v8::Local<v8::Function> callback = this->callback.Get(isolate);
507         v8::Local<v8::Object> receiver = this->receiver.Get(isolate);
508         v8::Local<v8::Context> context = this->context.Get(isolate);
509         v8::MaybeLocal<v8::Value> maybeResult = callback->Call(context, receiver, 0, {});
510         if (maybeResult.IsEmpty()) {
511             return v8::Undefined(isolate);
512         }
513         v8::Local<v8::Value> result;
514         if (!maybeResult.ToLocal(&result)) {
515             return v8::Undefined(isolate);
516         }
517         return result;
518     }
519 };
520 
521 struct NodeRuntime {
522     node::MultiIsolatePlatform* platform;
523     node::CommonEnvironmentSetup* setup;
524 
NodeRuntimeNodeRuntime525     NodeRuntime() : platform(nullptr), setup(nullptr) {}
526 
initNodeRuntime527     bool init(bool inspect) {
528         std::vector<std::string> args{"--expose-gc"};
529         if (inspect) args.push_back("--inspect");
530         std::vector<std::string> exec_args;
531         std::vector<std::string> errors;
532         int exit_code = node::InitializeNodeWithArgs(&args, &exec_args, &errors);
533         assert(exit_code == 0);
534         platform = node::MultiIsolatePlatform::Create(4).release();
535         v8::V8::InitializePlatform(platform);
536         v8::V8::Initialize();
537         this->setup = node::CommonEnvironmentSetup::Create(
538             platform, &errors, args, exec_args
539         ).release();
540         if (setup == nullptr) return false;
541         node::SetProcessExitHandler(setup->env(), [](node::Environment* env, int errorCode) {
542             fprintf(stderr, "Trying to exit with code = %d", errorCode);
543         });
544         return true;
545     }
546 
deinitNodeRuntime547     void deinit() {
548         node::Stop(setup->env());
549         delete setup;
550         delete platform;
551         v8::V8::Dispose();
552         v8::V8::DisposePlatform();
553     }
554 };