Skip to content

Commit 58feec5

Browse files
authored
feat: add sendMessageSync API (#100)
* feat: add sendMessageSync API Signed-off-by: Hosung Kim [email protected] * apply review comments --------- Signed-off-by: Hosung Kim [email protected]
1 parent 71b4d72 commit 58feec5

File tree

10 files changed

+247
-14
lines changed

10 files changed

+247
-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+
sendMessageSync: (message) => {
99+
if (typeof message !== "string") {
100+
throw new TypeError("The message argument must be a string");
101+
}
102+
103+
if (binding.sendMessageSync) {
104+
return binding.sendMessageSync(message);
105+
}
106+
}
98107
};
99108

100109
setupMessagePort(object, binding);

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

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,36 @@
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::SendMessageSyncCallback send_message_sync_callback{nullptr};
33+
void* send_message_sync_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 sendMessageSync callback to isolate context embedder data.
44+
runner_.SetOnMainEnvCreationCallback(
45+
[this](v8::Local<v8::Context> context) {
46+
context->SetAlignedPointerInEmbedderData(
47+
LWNode::ContextEmbedderIndex::kSendMessageSyncCallback,
48+
reinterpret_cast<void*>(
49+
config_.internal_->send_message_sync_callback));
50+
context->SetAlignedPointerInEmbedderData(
51+
LWNode::ContextEmbedderIndex::kSendMessageSyncCallbackData,
52+
config_.internal_->send_message_sync_callback_data);
53+
});
54+
3655
return InitializeNode(argc, argv, &instance_);
3756
}
3857

@@ -53,11 +72,18 @@ class Runtime::Internal {
5372
private:
5473
NodeMainInstance* instance_{nullptr};
5574
LWNode::LWNodeMainRunner runner_;
75+
Runtime::Configuration config_;
5676
bool is_initialized{false};
5777
};
5878

59-
Runtime::Runtime() {
60-
internal_ = new Internal();
79+
/**************************************************************************
80+
* Runtime class
81+
**************************************************************************/
82+
83+
Runtime::Runtime() : internal_(new Internal()) {}
84+
85+
Runtime::Runtime(Configuration&& config) : Runtime() {
86+
internal_->config_ = std::move(config);
6187
}
6288

6389
Runtime::~Runtime() {
@@ -83,6 +109,35 @@ std::shared_ptr<Port> Runtime::GetPort() {
83109
return internal_->runner_.GetPort();
84110
}
85111

112+
/**************************************************************************
113+
* Runtime::Configuration class
114+
**************************************************************************/
115+
116+
Runtime::Configuration::Configuration()
117+
: internal_(new Runtime::Configuration::Internal()) {}
118+
119+
Runtime::Configuration::~Configuration() {
120+
delete internal_;
121+
}
122+
123+
Runtime::Configuration& Runtime::Configuration::operator=(
124+
Configuration&& other) {
125+
delete internal_;
126+
internal_ = other.internal_;
127+
other.internal_ = nullptr;
128+
return *this;
129+
}
130+
131+
void Runtime::Configuration::OnSendMessageSync(
132+
Runtime::SendMessageSyncCallback callback, void* user_data) {
133+
internal_->send_message_sync_callback = callback;
134+
internal_->send_message_sync_callback_data = user_data;
135+
}
136+
137+
/**************************************************************************
138+
* Static functions
139+
**************************************************************************/
140+
86141
bool ParseAULEvent(int argc, char** argv) {
87142
bool result = AULEventReceiver::getInstance()->start(argc, argv);
88143
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_main_env_creation_callback_) {
131+
on_main_env_creation_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 SetOnMainEnvCreationCallback(
201+
const std::function<void(v8::Local<v8::Context>)>& callback) {
202+
on_main_env_creation_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_main_env_creation_callback_{
210+
nullptr};
200211
};
201212

202213
} // namespace LWNode

escargot.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
'-DESCARGOT_THREADING=<(escargot_threading)',
5555
'-DESCARGOT_ASAN=<(asan)',
5656
'-DESCARGOT_DEBUGGER=<(escargot_debugger)',
57+
'-DCMAKE_POLICY_VERSION_MINIMUM=3.5',
5758
],
5859
},
5960
'all_dependent_settings': {

include/lwnode/lwnode-public.h

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,34 +35,57 @@ 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 SendMessageSyncCallback = std::string (*)(const std::string&,
53+
void* user_data);
54+
55+
class Configuration {
56+
public:
57+
friend Runtime;
58+
Configuration();
59+
~Configuration();
60+
61+
Configuration(Configuration&) = delete;
62+
Configuration(Configuration&&) = delete;
63+
Configuration& operator=(const Configuration& t) = delete;
64+
Configuration& operator=(Configuration&&);
65+
66+
void OnSendMessageSync(SendMessageSyncCallback callback, void* user_data);
67+
68+
private:
69+
struct Internal;
70+
Internal* internal_ = nullptr;
71+
};
72+
5273
Runtime();
74+
Runtime(Configuration&& config);
75+
5376
~Runtime();
5477

5578
/**
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-
**/
79+
* Start the runtime and returns the exit code. It initializes the runtime
80+
* and runs it. When the runtime is initialized, the promise object is set.
81+
*
82+
* @param argc - Argument count.
83+
* @param argv - Argument vector. The element should be the starting file
84+
* name of the application.
85+
* @param promise - Promise object. It will be set when the runtime
86+
* initialization is complete.
87+
* @return Returns the exit code of the runtime.
88+
**/
6689
int Start(int argc, char** argv, std::promise<void>&& promise);
6790

6891
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+
kSendMessageSyncCallback = 92,
44+
kSendMessageSyncCallbackData = 93,
4345
};
4446

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

src/lwnode/lwnode.cc

Lines changed: 31 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,33 @@ static ValueRef* Unref(ExecutionStateRef* state,
240241
return ValueRef::create(loop_holder->ref_count());
241242
}
242243

244+
static ValueRef* SendMessageSync(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::SendMessageSyncCallback callback =
256+
reinterpret_cast<lwnode::Runtime::SendMessageSyncCallback>(
257+
lwContext->GetAlignedPointerFromEmbedderData(
258+
kSendMessageSyncCallback));
259+
if (!callback) {
260+
return ValueRef::createUndefined();
261+
}
262+
263+
void* data = lwContext->GetAlignedPointerFromEmbedderData(
264+
kSendMessageSyncCallbackData);
265+
266+
std::string response = callback(message, data);
267+
268+
return StringRef::createFromUTF8(response.c_str(), response.length());
269+
}
270+
243271
void InitMainMessagePort(Local<Context> context,
244272
MainMessagePort* main_port,
245273
LoopHolderUV* loop_holder,
@@ -277,6 +305,9 @@ void InitializeProcessMethods(Local<Object> target, Local<Context> context) {
277305

278306
SetMethod(esContext, esTarget, "ref", Ref);
279307
SetMethod(esContext, esTarget, "unref", Unref);
308+
309+
SetMethod(esContext, esTarget, "sendMessageSync", SendMessageSync);
310+
280311
ModuleMessagePortInit(esContext, esTarget);
281312
}
282313

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(send-message-sync.x send-message-sync.cc)

test/embedding/send-message-sync.cc

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

0 commit comments

Comments
 (0)