Skip to content

Commit 13414d1

Browse files
committed
feat: add binding API
Signed-off-by: Hosung Kim [email protected]
1 parent 5934f42 commit 13414d1

File tree

9 files changed

+248
-14
lines changed

9 files changed

+248
-14
lines changed

deps/node/lib/internal/lwnode/setup.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ function wrapLWNodeMethods(binding) {
9595
return binding.hasSystemInfo.apply(null, args);
9696
}
9797
},
98+
binding: (message) => {
99+
if (typeof message !== "string") {
100+
throw new TypeError("The message argument must be a string");
101+
}
102+
103+
if (binding.binding) {
104+
return binding.binding(message);
105+
}
106+
}
98107
};
99108

100109
setupMessagePort(object, binding);

deps/node/src/lwnode/lwnode-public.cc

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,34 @@
2222
#include "node.h"
2323
#include "node_main_lw_runner-inl.h"
2424
#include "trace.h"
25+
#include "v8.h"
2526

2627
using namespace node;
2728

2829
namespace lwnode {
2930

31+
struct Runtime::Configuration::Internal {
32+
Runtime::BindingCallback binding_callback{nullptr};
33+
void* binding_callback_data{nullptr};
34+
};
35+
3036
class Runtime::Internal {
3137
friend Runtime;
3238

3339
public:
3440
std::pair<bool, int> Init(int argc, char** argv) {
3541
is_initialized = true;
42+
43+
// Set binding callback to isolate context embedder data.
44+
runner_.SetOnCreatedContextCallback([this](v8::Local<v8::Context> context) {
45+
context->SetAlignedPointerInEmbedderData(
46+
LWNode::ContextEmbedderIndex::kBindingCallback,
47+
reinterpret_cast<void*>(config_.internal_->binding_callback));
48+
context->SetAlignedPointerInEmbedderData(
49+
LWNode::ContextEmbedderIndex::kBindingCallbackData,
50+
config_.internal_->binding_callback_data);
51+
});
52+
3653
return InitializeNode(argc, argv, &instance_);
3754
}
3855

@@ -53,11 +70,18 @@ class Runtime::Internal {
5370
private:
5471
NodeMainInstance* instance_{nullptr};
5572
LWNode::LWNodeMainRunner runner_;
73+
Runtime::Configuration config_;
5674
bool is_initialized{false};
5775
};
5876

59-
Runtime::Runtime() {
60-
internal_ = new Internal();
77+
/**************************************************************************
78+
* Runtime class
79+
**************************************************************************/
80+
81+
Runtime::Runtime() : internal_(new Internal()) {}
82+
83+
Runtime::Runtime(Configuration&& config) : Runtime() {
84+
internal_->config_ = std::move(config);
6185
}
6286

6387
Runtime::~Runtime() {
@@ -83,6 +107,35 @@ std::shared_ptr<Port> Runtime::GetPort() {
83107
return internal_->runner_.GetPort();
84108
}
85109

110+
/**************************************************************************
111+
* Runtime::Configuration class
112+
**************************************************************************/
113+
114+
Runtime::Configuration::Configuration()
115+
: internal_(new Runtime::Configuration::Internal()) {}
116+
117+
Runtime::Configuration::~Configuration() {
118+
delete internal_;
119+
}
120+
121+
Runtime::Configuration& Runtime::Configuration::operator=(
122+
Configuration&& other) {
123+
delete internal_;
124+
internal_ = other.internal_;
125+
other.internal_ = nullptr;
126+
return *this;
127+
}
128+
129+
void Runtime::Configuration::SetBindingCallback(
130+
Runtime::BindingCallback callback, void* user_data) {
131+
internal_->binding_callback = callback;
132+
internal_->binding_callback_data = user_data;
133+
}
134+
135+
/**************************************************************************
136+
* Static functions
137+
**************************************************************************/
138+
86139
bool ParseAULEvent(int argc, char** argv) {
87140
bool result = AULEventReceiver::getInstance()->start(argc, argv);
88141
if (result) {

deps/node/src/node_main_lw_runner-inl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,10 @@ class LWNodeMainRunner {
127127

128128
Context::Scope context_scope(env_->context());
129129

130+
if (on_context_created_callback_) {
131+
on_context_created_callback_(env_->context());
132+
}
133+
130134
if (exit_code == 0) {
131135
LoadEnvironment(env_.get());
132136

@@ -193,10 +197,17 @@ class LWNodeMainRunner {
193197
promise_ = std::move(promise);
194198
}
195199

200+
void SetOnCreatedContextCallback(
201+
const std::function<void(v8::Local<v8::Context>)>& callback) {
202+
on_context_created_callback_ = callback;
203+
}
204+
196205
private:
197206
std::unique_ptr<node::ArrayBufferAllocator> array_buffer_allocator_;
198207
Environment* environment_ = nullptr;
199208
std::promise<void> promise_;
209+
std::function<void(v8::Local<v8::Context>)> on_context_created_callback_{
210+
nullptr};
200211
};
201212

202213
} // namespace LWNode

include/lwnode/lwnode-public.h

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,34 +35,56 @@ LWNODE_EXPORT bool ParseAULEvent(int argc, char** argv);
3535
* Sets the path of the root directory of the JavaScript. If you do
3636
* not put the path argument, the root path is the app's resource path by
3737
* default on Tizen AUL mode. Be sure to call this function before lwnode::Start
38-
* function.
38+
* function.
3939
**/
4040
LWNODE_EXPORT bool InitScriptRootPath(const std::string path = "");
4141

4242
LWNODE_EXPORT int Start(int argc, char** argv);
4343

4444
/**
4545
* Sets the dlog tag id for debugging. This is only used on Tizen when not in
46-
* AUL mode.
46+
* AUL mode.
4747
**/
4848
LWNODE_EXPORT void SetDlogID(const std::string& appId);
4949

5050
class LWNODE_EXPORT Runtime {
5151
public:
52+
using BindingCallback = std::string (*)(const std::string&, void* user_data);
53+
54+
class Configuration {
55+
public:
56+
friend Runtime;
57+
Configuration();
58+
~Configuration();
59+
60+
Configuration(Configuration&) = delete;
61+
Configuration(Configuration&&) = delete;
62+
Configuration& operator=(const Configuration& t) = delete;
63+
Configuration& operator=(Configuration&&);
64+
65+
void SetBindingCallback(BindingCallback callback, void* user_data);
66+
67+
private:
68+
struct Internal;
69+
Internal* internal_ = nullptr;
70+
};
71+
5272
Runtime();
73+
Runtime(Configuration&& config);
74+
5375
~Runtime();
5476

5577
/**
56-
* Start the runtime and returns the exit code. It initializes the runtime
57-
* and runs it. When the runtime is initialized, the promise object is set.
58-
*
59-
* @param argc - Argument count.
60-
* @param argv - Argument vector. The element should be the starting file
61-
* name of the application.
62-
* @param promise - Promise object. It will be set when the runtime
63-
* initialization is complete.
64-
* @return Returns the exit code of the runtime.
65-
**/
78+
* Start the runtime and returns the exit code. It initializes the runtime
79+
* and runs it. When the runtime is initialized, the promise object is set.
80+
*
81+
* @param argc - Argument count.
82+
* @param argv - Argument vector. The element should be the starting file
83+
* name of the application.
84+
* @param promise - Promise object. It will be set when the runtime
85+
* initialization is complete.
86+
* @return Returns the exit code of the runtime.
87+
**/
6688
int Start(int argc, char** argv, std::promise<void>&& promise);
6789

6890
std::shared_ptr<Port> GetPort();

include/lwnode/lwnode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ enum ContextEmbedderIndex {
4040
// Others are listed in deps/node/src/node_context_data.h.
4141
kMainMessagePort = 90,
4242
kLoopHolder = 91,
43+
kBindingCallback = 92,
44+
kBindingCallbackData = 93,
4345
};
4446

4547
void InitializeProcessMethods(v8::Local<v8::Object> target,

src/lwnode/lwnode.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "api/utils/misc.h"
2929
#include "api/utils/smaps.h"
3030
#include "base.h"
31+
#include "lwnode-public.h"
3132
#include "lwnode/lwnode-gc-strategy.h"
3233
#include "lwnode/lwnode-loader.h"
3334

@@ -240,6 +241,32 @@ static ValueRef* Unref(ExecutionStateRef* state,
240241
return ValueRef::create(loop_holder->ref_count());
241242
}
242243

244+
static ValueRef* Binding(ExecutionStateRef* state,
245+
ValueRef* this_value,
246+
size_t argc,
247+
ValueRef** argv,
248+
bool isConstructCall) {
249+
std::string message;
250+
if (argc > 0 && argv[0]->isString()) {
251+
message = argv[0]->asString()->toStdUTF8String();
252+
}
253+
254+
ContextWrap* lwContext = ContextWrap::fromEscargot(state->context());
255+
lwnode::Runtime::BindingCallback callback =
256+
reinterpret_cast<lwnode::Runtime::BindingCallback>(
257+
lwContext->GetAlignedPointerFromEmbedderData(kBindingCallback));
258+
259+
void* data =
260+
lwContext->GetAlignedPointerFromEmbedderData(kBindingCallbackData);
261+
262+
std::string response;
263+
if (callback) {
264+
response = callback(message, data);
265+
}
266+
267+
return StringRef::createFromUTF8(response.c_str(), response.length());
268+
}
269+
243270
void InitMainMessagePort(Local<Context> context,
244271
MainMessagePort* main_port,
245272
LoopHolderUV* loop_holder,
@@ -277,6 +304,9 @@ void InitializeProcessMethods(Local<Object> target, Local<Context> context) {
277304

278305
SetMethod(esContext, esTarget, "ref", Ref);
279306
SetMethod(esContext, esTarget, "unref", Unref);
307+
308+
SetMethod(esContext, esTarget, "binding", Binding);
309+
280310
ModuleMessagePortInit(esContext, esTarget);
281311
}
282312

test/embedding/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ endif()
1515

1616
add_executable(embedtest.x embedtest.cc)
1717
add_executable(example.x example.cc)
18+
add_executable(binding.x binding.cc)

test/embedding/binding.cc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <filesystem>
2+
#include <future>
3+
#include <iostream>
4+
#include <thread>
5+
#include <memory>
6+
#include <lwnode-public.h>
7+
#include <message-port.h>
8+
9+
#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))
10+
11+
class Info {
12+
public:
13+
Info(std::string name, std::string age, std::string gender)
14+
: name_(name), age_(age), gender_(gender) {}
15+
16+
std::string GetName() const { return name_; }
17+
std::string GetAge() const { return age_; }
18+
std::string GetGender() const { return gender_; }
19+
20+
private:
21+
std::string name_;
22+
std::string age_;
23+
std::string gender_;
24+
};
25+
26+
int main(int argc, char* argv[]) {
27+
std::shared_ptr<Info> info = std::make_shared<Info>("John", "30", "male");
28+
29+
lwnode::Runtime::Configuration configuration;
30+
configuration.SetBindingCallback(
31+
[](const std::string& message, void* user_data) -> std::string {
32+
Info* info = static_cast<Info*>(user_data);
33+
34+
if (message == "name")
35+
return info->GetName();
36+
if (message == "age")
37+
return info->GetAge();
38+
if (message == "gender")
39+
return info->GetGender();
40+
41+
return "";
42+
},
43+
(void*)info.get());
44+
45+
auto runtime = std::make_shared<lwnode::Runtime>(std::move(configuration));
46+
47+
std::promise<void> promise;
48+
std::future<void> init_future = promise.get_future();
49+
const char* script = "test/embedding/test-10-binding-basic.js";
50+
std::string path = (std::filesystem::current_path() / script).string();
51+
char* args[] = {const_cast<char*>(""), const_cast<char*>(path.c_str())};
52+
53+
std::thread worker = std::thread(
54+
[&](std::promise<void>&& promise) mutable {
55+
// FIXME: Fix Runtime::Init() call to ensure environment initialization
56+
// before running the loop, Runtime::Run(). This workaround passes a
57+
// promise directly to know when that is.
58+
runtime->Start(COUNT_OF(args), args, std::move(promise));
59+
},
60+
std::move(promise));
61+
62+
init_future.wait();
63+
64+
int count1 = 0;
65+
auto port2 = runtime->GetPort();
66+
port2->OnMessage([&](const MessageEvent* event) {
67+
std::cout << event->data() << std::endl;
68+
count1++;
69+
});
70+
port2->PostMessage(MessageEvent::New("ping"));
71+
72+
worker.join();
73+
return 0;
74+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const lwnode = process.lwnode;
2+
const port = process.lwnode.port;
3+
4+
lwnode.ref();
5+
6+
port.onmessage = (event) => {
7+
console.log(`${event.data}`);
8+
if (event.data == "ping") {
9+
port.postMessage("pong");
10+
lwnode.unref();
11+
}
12+
};
13+
14+
function printMessage() {
15+
console.log("printMessage called--------------------------------------------");
16+
const name = lwnode.binding('name');
17+
console.log(`Hello, ${name}!`);
18+
19+
const age = lwnode.binding('age');
20+
console.log(`I am ${age} years old.`);
21+
22+
const gender = lwnode.binding('gender');
23+
console.log(`My gender is ${gender}.`);
24+
}
25+
26+
let count = 10;
27+
let loop = setInterval(() => {
28+
if (count-- <= 0) {
29+
clearInterval(loop);
30+
}
31+
printMessage();
32+
}, 1000);

0 commit comments

Comments
 (0)