Skip to content

Commit 643958b

Browse files
authored
feat: use node module bindings like the iOS runtime (#1795)
1 parent b248dc4 commit 643958b

11 files changed

+836
-107
lines changed

test-app/runtime/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ add_library(
115115
src/main/cpp/MetadataReader.cpp
116116
src/main/cpp/MetadataTreeNode.cpp
117117
src/main/cpp/MethodCache.cpp
118+
src/main/cpp/ModuleBinding.cpp
118119
src/main/cpp/ModuleInternal.cpp
119120
src/main/cpp/NativeScriptException.cpp
120121
src/main/cpp/NumericCasts.cpp

test-app/runtime/src/main/cpp/IsolateDisposer.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,19 @@
99
#include <console/Console.h>
1010

1111

12+
1213
namespace tns {
1314
void disposeIsolate(v8::Isolate *isolate) {
1415
tns::ArgConverter::onDisposeIsolate(isolate);
1516
tns::MetadataNode::onDisposeIsolate(isolate);
1617
tns::V8GlobalHelpers::onDisposeIsolate(isolate);
1718
tns::Console::onDisposeIsolate(isolate);
19+
// clear all isolate bound objects
20+
std::lock_guard<std::mutex> lock(isolateBoundObjectsMutex_);
21+
auto it = isolateBoundObjects_.find(isolate);
22+
if (it != isolateBoundObjects_.end()) {
23+
it->second->clear();
24+
isolateBoundObjects_.erase(it);
25+
}
1826
}
1927
}

test-app/runtime/src/main/cpp/IsolateDisposer.h

+27
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,36 @@
55
#ifndef TEST_APP_ISOLATEDISPOSER_H
66
#define TEST_APP_ISOLATEDISPOSER_H
77
#include "v8.h"
8+
#include "robin_hood.h"
9+
#include <vector>
10+
#include <mutex>
11+
#include <memory>
812

913
namespace tns {
1014
void disposeIsolate(v8::Isolate* isolate);
15+
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
16+
template<typename T>
17+
auto unique_void(T * ptr) -> unique_void_ptr
18+
{
19+
return unique_void_ptr(ptr, [](void const * data) {
20+
T const * p = static_cast<T const*>(data);
21+
delete p;
22+
});
23+
}
24+
robin_hood::unordered_map<v8::Isolate*, std::shared_ptr<std::vector<unique_void_ptr>>> isolateBoundObjects_;
25+
std::mutex isolateBoundObjectsMutex_;
26+
template<typename T>
27+
void registerIsolateBoundObject(v8::Isolate* isolate, T *ptr) {
28+
std::lock_guard<std::mutex> lock(isolateBoundObjectsMutex_);
29+
auto it = isolateBoundObjects_.find(isolate);
30+
if (it == isolateBoundObjects_.end()) {
31+
auto vec = std::make_shared<std::vector<unique_void_ptr>>();
32+
vec->push_back(unique_void(ptr));
33+
isolateBoundObjects_.emplace(isolate, vec);
34+
} else {
35+
it->second->push_back(unique_void(ptr));
36+
}
37+
}
1138
}
1239

1340
#endif //TEST_APP_ISOLATEDISPOSER_H
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//
2+
// ModuleBinding.cpp
3+
// NativeScript
4+
//
5+
// Created by Eduardo Speroni on 5/11/23.
6+
// Copyright © 2023 Progress. All rights reserved.
7+
//
8+
9+
#include "ModuleBinding.h"
10+
11+
// TODO: add here
12+
//#define NSC_BUILTIN_STANDARD_BINDINGS(V) \
13+
//V(fs)
14+
15+
#define NSC_BUILTIN_STANDARD_BINDINGS(V)
16+
17+
18+
#define NSC_BUILTIN_BINDINGS(V) \
19+
NSC_BUILTIN_STANDARD_BINDINGS(V)
20+
21+
// This is used to load built-in bindings. Instead of using
22+
// __attribute__((constructor)), we call the _register_<modname>
23+
// function for each built-in bindings explicitly in
24+
// binding::RegisterBuiltinBindings(). This is only forward declaration.
25+
// The definitions are in each binding's implementation when calling
26+
// the NODE_BINDING_CONTEXT_AWARE_INTERNAL.
27+
#define V(modname) void _register_##modname();
28+
NSC_BUILTIN_BINDINGS(V)
29+
#undef V
30+
31+
32+
33+
34+
#define V(modname) \
35+
void _register_isolate_##modname(v8::Isolate* isolate, \
36+
v8::Local<v8::FunctionTemplate> target);
37+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
38+
#undef V
39+
40+
41+
42+
using v8::Context;
43+
using v8::EscapableHandleScope;
44+
using v8::Exception;
45+
using v8::FunctionCallbackInfo;
46+
using v8::FunctionTemplate;
47+
using v8::HandleScope;
48+
using v8::Isolate;
49+
using v8::Local;
50+
using v8::Object;
51+
using v8::String;
52+
using v8::Value;
53+
54+
namespace tns {
55+
// Globals per process
56+
static ns_module* modlist_internal;
57+
static ns_module* modlist_linked;
58+
static thread_local ns_module* thread_local_modpending;
59+
bool node_is_initialized = false;
60+
61+
extern "C" void nativescript_module_register(void* m) {
62+
struct ns_module* mp = reinterpret_cast<struct ns_module*>(m);
63+
64+
if (mp->nm_flags & NM_F_INTERNAL) {
65+
mp->nm_link = modlist_internal;
66+
modlist_internal = mp;
67+
} else if (!node_is_initialized) {
68+
// "Linked" modules are included as part of the node project.
69+
// Like builtins they are registered *before* node::Init runs.
70+
mp->nm_flags = NM_F_LINKED;
71+
mp->nm_link = modlist_linked;
72+
modlist_linked = mp;
73+
} else {
74+
thread_local_modpending = mp;
75+
}
76+
}
77+
78+
namespace binding {
79+
80+
void RegisterBuiltinBindings() {
81+
#define V(modname) _register_##modname();
82+
NSC_BUILTIN_BINDINGS(V)
83+
#undef V
84+
}
85+
86+
void CreateInternalBindingTemplates(v8::Isolate* isolate, Local<FunctionTemplate> templ) {
87+
#define V(modname) \
88+
do { \
89+
/*templ->InstanceTemplate()->SetInternalFieldCount( \
90+
BaseObject::kInternalFieldCount);*/ \
91+
_register_isolate_##modname(isolate, templ); \
92+
/*isolate_data->set_##modname##_binding(templ);*/ \
93+
} while (0);
94+
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
95+
#undef V
96+
}
97+
98+
};
99+
100+
};
101+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//
2+
// ModuleBinding.hpp
3+
// NativeScript
4+
//
5+
// Created by Eduardo Speroni on 5/11/23.
6+
// Copyright © 2023 Progress. All rights reserved.
7+
//
8+
9+
#ifndef ModuleBinding_hpp
10+
#define ModuleBinding_hpp
11+
12+
#include "v8.h"
13+
14+
15+
namespace tns {
16+
17+
#define NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \
18+
static tns::ns_module _module = { \
19+
NODE_MODULE_VERSION, \
20+
flags, \
21+
nullptr, \
22+
__FILE__, \
23+
nullptr, \
24+
(tns::addon_context_register_func)(regfunc), \
25+
NODE_STRINGIFY(modname), \
26+
priv, \
27+
nullptr}; \
28+
void _register_##modname() { node_module_register(&_module); }
29+
30+
#define NODE_BINDING_CONTEXT_AWARE_INTERNAL(modname, regfunc) \
31+
NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL)
32+
33+
34+
35+
36+
37+
38+
39+
40+
#define NODE_BINDING_PER_ISOLATE_INIT(modname, per_isolate_func) \
41+
void _register_isolate_##modname(v8::Isolate* isolate, \
42+
v8::Local<v8::FunctionTemplate> target) { \
43+
per_isolate_func(isolate, target); \
44+
}
45+
46+
47+
// This is a helepr that gives the target as an ObjectTemplate (using target.PrototypeTemplate())
48+
49+
#define NODE_BINDING_PER_ISOLATE_INIT_OBJ(modname, per_isolate_func) \
50+
void _register_isolate_##modname(v8::Isolate* isolate, \
51+
v8::Local<v8::FunctionTemplate> target) { \
52+
per_isolate_func(isolate, target->PrototypeTemplate()); \
53+
}
54+
55+
56+
#define NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V) \
57+
V(timers)
58+
59+
enum {
60+
NM_F_BUILTIN = 1 << 0, // Unused.
61+
NM_F_LINKED = 1 << 1,
62+
NM_F_INTERNAL = 1 << 2,
63+
NM_F_DELETEME = 1 << 3,
64+
};
65+
66+
typedef void (*addon_register_func)(
67+
v8::Local<v8::Object> exports,
68+
v8::Local<v8::Value> module,
69+
void* priv);
70+
71+
typedef void (*addon_context_register_func)(
72+
v8::Local<v8::Object> exports,
73+
v8::Local<v8::Value> module,
74+
v8::Local<v8::Context> context,
75+
void* priv);
76+
77+
struct ns_module {
78+
int nm_version;
79+
unsigned int nm_flags;
80+
void* nm_dso_handle;
81+
const char* nm_filename;
82+
tns::addon_register_func nm_register_func;
83+
tns::addon_context_register_func nm_context_register_func;
84+
const char* nm_modname;
85+
void* nm_priv;
86+
struct ns_module* nm_link;
87+
};
88+
89+
namespace binding {
90+
void RegisterBuiltinBindings();
91+
void CreateInternalBindingTemplates(v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> templ);
92+
};
93+
94+
95+
};
96+
97+
98+
#endif /* ModuleBinding_hpp */

test-app/runtime/src/main/cpp/Runtime.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <unistd.h>
3131
#include <thread>
3232
#include "File.h"
33+
#include "ModuleBinding.h"
3334

3435
#ifdef APPLICATION_IN_DEBUG
3536
// #include "NetworkDomainCallbackHandlers.h"
@@ -500,6 +501,7 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath, const string& native
500501

501502
auto globalFunctionTemplate = FunctionTemplate::New(isolate);
502503
globalFunctionTemplate->SetClassName(ArgConverter::ConvertToV8String(isolate, "NativeScriptGlobalObject"));
504+
tns::binding::CreateInternalBindingTemplates(isolate, globalFunctionTemplate);
503505
auto globalTemplate = ObjectTemplate::New(isolate, globalFunctionTemplate);
504506

505507
const auto readOnlyFlags = static_cast<PropertyAttribute>(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
@@ -582,8 +584,6 @@ Isolate* Runtime::PrepareV8Runtime(const string& filesPath, const string& native
582584

583585
CallbackHandlers::CreateGlobalCastFunctions(isolate, globalTemplate);
584586

585-
m_timers.Init(isolate, globalTemplate);
586-
587587
Local<Context> context = Context::New(isolate, nullptr, globalTemplate);
588588

589589
auto global = context->Global();

test-app/runtime/src/main/cpp/Runtime.h

-2
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ class Runtime {
9494

9595
WeakRef m_weakRef;
9696

97-
Timers m_timers;
98-
9997
Profiler m_profiler;
10098

10199
MessageLoopTimer* m_loopTimer;

test-app/runtime/src/main/cpp/Timers.cpp

+22-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
#include <android/looper.h>
66
#include <unistd.h>
77
#include <thread>
8+
#include "ModuleBinding.h"
9+
#include "IsolateDisposer.h"
10+
#include "Util.h"
811

912

1013
/**
@@ -15,7 +18,6 @@
1518
* ALL changes and scheduling of a TimerTask MUST be done when locked in an isolate to ensure consistency
1619
*/
1720

18-
using namespace tns;
1921
using namespace v8;
2022

2123
// Takes a value and transform into a positive number
@@ -43,22 +45,18 @@ static double now_ms() {
4345
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
4446
}
4547

48+
namespace tns {
49+
50+
51+
4652

4753
void Timers::Init(v8::Isolate *isolate, v8::Local<v8::ObjectTemplate> &globalObjectTemplate) {
4854
isolate_ = isolate;
4955
// TODO: remove the __ns__ prefix once this is validated
50-
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__setTimeout"),
51-
FunctionTemplate::New(isolate, SetTimeoutCallback,
52-
External::New(isolate, this)));
53-
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__setInterval"),
54-
FunctionTemplate::New(isolate, SetIntervalCallback,
55-
External::New(isolate, this)));
56-
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__clearTimeout"),
57-
FunctionTemplate::New(isolate, ClearTimer,
58-
External::New(isolate, this)));
59-
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__clearInterval"),
60-
FunctionTemplate::New(isolate, ClearTimer,
61-
External::New(isolate, this)));
56+
SetMethod(isolate, globalObjectTemplate, "__ns__setTimeout", SetTimeoutCallback, External::New(isolate, this));
57+
SetMethod(isolate, globalObjectTemplate, "__ns__setInterval", SetIntervalCallback, External::New(isolate, this));
58+
SetMethod(isolate, globalObjectTemplate, "__ns__clearTimeout", ClearTimer, External::New(isolate, this));
59+
SetMethod(isolate, globalObjectTemplate, "__ns__clearInterval", ClearTimer, External::New(isolate, this));
6260
auto res = pipe(fd_);
6361
assert(res != -1);
6462
res = fcntl(fd_[1], F_SETFL, O_NONBLOCK);
@@ -324,4 +322,14 @@ int Timers::PumpTimerLoopCallback(int fd, int events, void *data) {
324322
}
325323
thiz->bufferFull.notify_one();
326324
return 1;
327-
}
325+
}
326+
327+
void Timers::InitStatic(v8::Isolate* isolate, v8::Local<v8::ObjectTemplate> globalObjectTemplate) {
328+
auto timers = new Timers();
329+
timers->Init(isolate, globalObjectTemplate);
330+
registerIsolateBoundObject(isolate, timers);
331+
}
332+
333+
};
334+
335+
NODE_BINDING_PER_ISOLATE_INIT_OBJ(timers, tns::Timers::InitStatic);

test-app/runtime/src/main/cpp/Timers.h

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ namespace tns {
7676
*/
7777
void Init(v8::Isolate *isolate, v8::Local<v8::ObjectTemplate> &globalObjectTemplate);
7878

79+
static void InitStatic(v8::Isolate* isolate, v8::Local<v8::ObjectTemplate> globalObjectTemplate);
80+
7981
/**
8082
* Disposes the timers. This will clear all references and stop all thread.
8183
* MUST be called in the same thread Init was called

0 commit comments

Comments
 (0)