Skip to content

Commit

Permalink
feat: use node module bindings like the iOS runtime (#1795)
Browse files Browse the repository at this point in the history
  • Loading branch information
edusperoni committed Nov 29, 2023
1 parent b248dc4 commit 643958b
Show file tree
Hide file tree
Showing 11 changed files with 836 additions and 107 deletions.
1 change: 1 addition & 0 deletions test-app/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ add_library(
src/main/cpp/MetadataReader.cpp
src/main/cpp/MetadataTreeNode.cpp
src/main/cpp/MethodCache.cpp
src/main/cpp/ModuleBinding.cpp
src/main/cpp/ModuleInternal.cpp
src/main/cpp/NativeScriptException.cpp
src/main/cpp/NumericCasts.cpp
Expand Down
8 changes: 8 additions & 0 deletions test-app/runtime/src/main/cpp/IsolateDisposer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,19 @@
#include <console/Console.h>



namespace tns {
void disposeIsolate(v8::Isolate *isolate) {
tns::ArgConverter::onDisposeIsolate(isolate);
tns::MetadataNode::onDisposeIsolate(isolate);
tns::V8GlobalHelpers::onDisposeIsolate(isolate);
tns::Console::onDisposeIsolate(isolate);
// clear all isolate bound objects
std::lock_guard<std::mutex> lock(isolateBoundObjectsMutex_);
auto it = isolateBoundObjects_.find(isolate);
if (it != isolateBoundObjects_.end()) {
it->second->clear();
isolateBoundObjects_.erase(it);
}
}
}
27 changes: 27 additions & 0 deletions test-app/runtime/src/main/cpp/IsolateDisposer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,36 @@
#ifndef TEST_APP_ISOLATEDISPOSER_H
#define TEST_APP_ISOLATEDISPOSER_H
#include "v8.h"
#include "robin_hood.h"
#include <vector>
#include <mutex>
#include <memory>

namespace tns {
void disposeIsolate(v8::Isolate* isolate);
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
delete p;
});
}
robin_hood::unordered_map<v8::Isolate*, std::shared_ptr<std::vector<unique_void_ptr>>> isolateBoundObjects_;
std::mutex isolateBoundObjectsMutex_;
template<typename T>
void registerIsolateBoundObject(v8::Isolate* isolate, T *ptr) {
std::lock_guard<std::mutex> lock(isolateBoundObjectsMutex_);
auto it = isolateBoundObjects_.find(isolate);
if (it == isolateBoundObjects_.end()) {
auto vec = std::make_shared<std::vector<unique_void_ptr>>();
vec->push_back(unique_void(ptr));
isolateBoundObjects_.emplace(isolate, vec);
} else {
it->second->push_back(unique_void(ptr));
}
}
}

#endif //TEST_APP_ISOLATEDISPOSER_H
101 changes: 101 additions & 0 deletions test-app/runtime/src/main/cpp/ModuleBinding.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//
// ModuleBinding.cpp
// NativeScript
//
// Created by Eduardo Speroni on 5/11/23.
// Copyright © 2023 Progress. All rights reserved.
//

#include "ModuleBinding.h"

// TODO: add here
//#define NSC_BUILTIN_STANDARD_BINDINGS(V) \
//V(fs)

#define NSC_BUILTIN_STANDARD_BINDINGS(V)


#define NSC_BUILTIN_BINDINGS(V) \
NSC_BUILTIN_STANDARD_BINDINGS(V)

// This is used to load built-in bindings. Instead of using
// __attribute__((constructor)), we call the _register_<modname>
// function for each built-in bindings explicitly in
// binding::RegisterBuiltinBindings(). This is only forward declaration.
// The definitions are in each binding's implementation when calling
// the NODE_BINDING_CONTEXT_AWARE_INTERNAL.
#define V(modname) void _register_##modname();
NSC_BUILTIN_BINDINGS(V)
#undef V




#define V(modname) \
void _register_isolate_##modname(v8::Isolate* isolate, \
v8::Local<v8::FunctionTemplate> target);
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
#undef V



using v8::Context;
using v8::EscapableHandleScope;
using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

namespace tns {
// Globals per process
static ns_module* modlist_internal;
static ns_module* modlist_linked;
static thread_local ns_module* thread_local_modpending;
bool node_is_initialized = false;

extern "C" void nativescript_module_register(void* m) {
struct ns_module* mp = reinterpret_cast<struct ns_module*>(m);

if (mp->nm_flags & NM_F_INTERNAL) {
mp->nm_link = modlist_internal;
modlist_internal = mp;
} else if (!node_is_initialized) {
// "Linked" modules are included as part of the node project.
// Like builtins they are registered *before* node::Init runs.
mp->nm_flags = NM_F_LINKED;
mp->nm_link = modlist_linked;
modlist_linked = mp;
} else {
thread_local_modpending = mp;
}
}

namespace binding {

void RegisterBuiltinBindings() {
#define V(modname) _register_##modname();
NSC_BUILTIN_BINDINGS(V)
#undef V
}

void CreateInternalBindingTemplates(v8::Isolate* isolate, Local<FunctionTemplate> templ) {
#define V(modname) \
do { \
/*templ->InstanceTemplate()->SetInternalFieldCount( \
BaseObject::kInternalFieldCount);*/ \
_register_isolate_##modname(isolate, templ); \
/*isolate_data->set_##modname##_binding(templ);*/ \
} while (0);
NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V)
#undef V
}

};

};

98 changes: 98 additions & 0 deletions test-app/runtime/src/main/cpp/ModuleBinding.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// ModuleBinding.hpp
// NativeScript
//
// Created by Eduardo Speroni on 5/11/23.
// Copyright © 2023 Progress. All rights reserved.
//

#ifndef ModuleBinding_hpp
#define ModuleBinding_hpp

#include "v8.h"


namespace tns {

#define NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \
static tns::ns_module _module = { \
NODE_MODULE_VERSION, \
flags, \
nullptr, \
__FILE__, \
nullptr, \
(tns::addon_context_register_func)(regfunc), \
NODE_STRINGIFY(modname), \
priv, \
nullptr}; \
void _register_##modname() { node_module_register(&_module); }

#define NODE_BINDING_CONTEXT_AWARE_INTERNAL(modname, regfunc) \
NODE_BINDING_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL)








#define NODE_BINDING_PER_ISOLATE_INIT(modname, per_isolate_func) \
void _register_isolate_##modname(v8::Isolate* isolate, \
v8::Local<v8::FunctionTemplate> target) { \
per_isolate_func(isolate, target); \
}


// This is a helepr that gives the target as an ObjectTemplate (using target.PrototypeTemplate())

#define NODE_BINDING_PER_ISOLATE_INIT_OBJ(modname, per_isolate_func) \
void _register_isolate_##modname(v8::Isolate* isolate, \
v8::Local<v8::FunctionTemplate> target) { \
per_isolate_func(isolate, target->PrototypeTemplate()); \
}


#define NODE_BINDINGS_WITH_PER_ISOLATE_INIT(V) \
V(timers)

enum {
NM_F_BUILTIN = 1 << 0, // Unused.
NM_F_LINKED = 1 << 1,
NM_F_INTERNAL = 1 << 2,
NM_F_DELETEME = 1 << 3,
};

typedef void (*addon_register_func)(
v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
void* priv);

typedef void (*addon_context_register_func)(
v8::Local<v8::Object> exports,
v8::Local<v8::Value> module,
v8::Local<v8::Context> context,
void* priv);

struct ns_module {
int nm_version;
unsigned int nm_flags;
void* nm_dso_handle;
const char* nm_filename;
tns::addon_register_func nm_register_func;
tns::addon_context_register_func nm_context_register_func;
const char* nm_modname;
void* nm_priv;
struct ns_module* nm_link;
};

namespace binding {
void RegisterBuiltinBindings();
void CreateInternalBindingTemplates(v8::Isolate* isolate, v8::Local<v8::FunctionTemplate> templ);
};


};


#endif /* ModuleBinding_hpp */
4 changes: 2 additions & 2 deletions test-app/runtime/src/main/cpp/Runtime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include <unistd.h>
#include <thread>
#include "File.h"
#include "ModuleBinding.h"

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

auto globalFunctionTemplate = FunctionTemplate::New(isolate);
globalFunctionTemplate->SetClassName(ArgConverter::ConvertToV8String(isolate, "NativeScriptGlobalObject"));
tns::binding::CreateInternalBindingTemplates(isolate, globalFunctionTemplate);
auto globalTemplate = ObjectTemplate::New(isolate, globalFunctionTemplate);

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

CallbackHandlers::CreateGlobalCastFunctions(isolate, globalTemplate);

m_timers.Init(isolate, globalTemplate);

Local<Context> context = Context::New(isolate, nullptr, globalTemplate);

auto global = context->Global();
Expand Down
2 changes: 0 additions & 2 deletions test-app/runtime/src/main/cpp/Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ class Runtime {

WeakRef m_weakRef;

Timers m_timers;

Profiler m_profiler;

MessageLoopTimer* m_loopTimer;
Expand Down
36 changes: 22 additions & 14 deletions test-app/runtime/src/main/cpp/Timers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
#include <android/looper.h>
#include <unistd.h>
#include <thread>
#include "ModuleBinding.h"
#include "IsolateDisposer.h"
#include "Util.h"


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

using namespace tns;
using namespace v8;

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

namespace tns {




void Timers::Init(v8::Isolate *isolate, v8::Local<v8::ObjectTemplate> &globalObjectTemplate) {
isolate_ = isolate;
// TODO: remove the __ns__ prefix once this is validated
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__setTimeout"),
FunctionTemplate::New(isolate, SetTimeoutCallback,
External::New(isolate, this)));
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__setInterval"),
FunctionTemplate::New(isolate, SetIntervalCallback,
External::New(isolate, this)));
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__clearTimeout"),
FunctionTemplate::New(isolate, ClearTimer,
External::New(isolate, this)));
globalObjectTemplate->Set(ArgConverter::ConvertToV8String(isolate, "__ns__clearInterval"),
FunctionTemplate::New(isolate, ClearTimer,
External::New(isolate, this)));
SetMethod(isolate, globalObjectTemplate, "__ns__setTimeout", SetTimeoutCallback, External::New(isolate, this));
SetMethod(isolate, globalObjectTemplate, "__ns__setInterval", SetIntervalCallback, External::New(isolate, this));
SetMethod(isolate, globalObjectTemplate, "__ns__clearTimeout", ClearTimer, External::New(isolate, this));
SetMethod(isolate, globalObjectTemplate, "__ns__clearInterval", ClearTimer, External::New(isolate, this));
auto res = pipe(fd_);
assert(res != -1);
res = fcntl(fd_[1], F_SETFL, O_NONBLOCK);
Expand Down Expand Up @@ -324,4 +322,14 @@ int Timers::PumpTimerLoopCallback(int fd, int events, void *data) {
}
thiz->bufferFull.notify_one();
return 1;
}
}

void Timers::InitStatic(v8::Isolate* isolate, v8::Local<v8::ObjectTemplate> globalObjectTemplate) {
auto timers = new Timers();
timers->Init(isolate, globalObjectTemplate);
registerIsolateBoundObject(isolate, timers);
}

};

NODE_BINDING_PER_ISOLATE_INIT_OBJ(timers, tns::Timers::InitStatic);
2 changes: 2 additions & 0 deletions test-app/runtime/src/main/cpp/Timers.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ namespace tns {
*/
void Init(v8::Isolate *isolate, v8::Local<v8::ObjectTemplate> &globalObjectTemplate);

static void InitStatic(v8::Isolate* isolate, v8::Local<v8::ObjectTemplate> globalObjectTemplate);

/**
* Disposes the timers. This will clear all references and stop all thread.
* MUST be called in the same thread Init was called
Expand Down
Loading

0 comments on commit 643958b

Please sign in to comment.