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