Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions deps/node/lib/internal/lwnode/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ function wrapLWNodeMethods(binding) {
return binding.hasSystemInfo.apply(null, args);
}
},
sendMessageSync: (message) => {
if (typeof message !== "string") {
throw new TypeError("The message argument must be a string");
}

if (binding.sendMessageSync) {
return binding.sendMessageSync(message);
}
}
};

setupMessagePort(object, binding);
Expand Down
59 changes: 57 additions & 2 deletions deps/node/src/lwnode/lwnode-public.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,36 @@
#include "node.h"
#include "node_main_lw_runner-inl.h"
#include "trace.h"
#include "v8.h"

using namespace node;

namespace lwnode {

struct Runtime::Configuration::Internal {
Runtime::SendMessageSyncCallback send_message_sync_callback{nullptr};
void* send_message_sync_callback_data{nullptr};
};

class Runtime::Internal {
friend Runtime;

public:
std::pair<bool, int> Init(int argc, char** argv) {
is_initialized = true;

// Set sendMessageSync callback to isolate context embedder data.
runner_.SetOnMainEnvCreationCallback(
[this](v8::Local<v8::Context> context) {
context->SetAlignedPointerInEmbedderData(
LWNode::ContextEmbedderIndex::kSendMessageSyncCallback,
reinterpret_cast<void*>(
config_.internal_->send_message_sync_callback));
context->SetAlignedPointerInEmbedderData(
LWNode::ContextEmbedderIndex::kSendMessageSyncCallbackData,
config_.internal_->send_message_sync_callback_data);
});

return InitializeNode(argc, argv, &instance_);
}

Expand All @@ -53,11 +72,18 @@ class Runtime::Internal {
private:
NodeMainInstance* instance_{nullptr};
LWNode::LWNodeMainRunner runner_;
Runtime::Configuration config_;
bool is_initialized{false};
};

Runtime::Runtime() {
internal_ = new Internal();
/**************************************************************************
* Runtime class
**************************************************************************/

Runtime::Runtime() : internal_(new Internal()) {}

Runtime::Runtime(Configuration&& config) : Runtime() {
internal_->config_ = std::move(config);
}

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

/**************************************************************************
* Runtime::Configuration class
**************************************************************************/

Runtime::Configuration::Configuration()
: internal_(new Runtime::Configuration::Internal()) {}

Runtime::Configuration::~Configuration() {
delete internal_;
}

Runtime::Configuration& Runtime::Configuration::operator=(
Configuration&& other) {
delete internal_;
internal_ = other.internal_;
other.internal_ = nullptr;
return *this;
}

void Runtime::Configuration::OnSendMessageSync(
Runtime::SendMessageSyncCallback callback, void* user_data) {
internal_->send_message_sync_callback = callback;
internal_->send_message_sync_callback_data = user_data;
}

/**************************************************************************
* Static functions
**************************************************************************/

bool ParseAULEvent(int argc, char** argv) {
bool result = AULEventReceiver::getInstance()->start(argc, argv);
if (result) {
Expand Down
11 changes: 11 additions & 0 deletions deps/node/src/node_main_lw_runner-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ class LWNodeMainRunner {

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

if (on_main_env_creation_callback_) {
on_main_env_creation_callback_(env_->context());
}

if (exit_code == 0) {
LoadEnvironment(env_.get());

Expand Down Expand Up @@ -193,10 +197,17 @@ class LWNodeMainRunner {
promise_ = std::move(promise);
}

void SetOnMainEnvCreationCallback(
const std::function<void(v8::Local<v8::Context>)>& callback) {
on_main_env_creation_callback_ = callback;
}

private:
std::unique_ptr<node::ArrayBufferAllocator> array_buffer_allocator_;
Environment* environment_ = nullptr;
std::promise<void> promise_;
std::function<void(v8::Local<v8::Context>)> on_main_env_creation_callback_{
nullptr};
};

} // namespace LWNode
1 change: 1 addition & 0 deletions escargot.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
'-DESCARGOT_THREADING=<(escargot_threading)',
'-DESCARGOT_ASAN=<(asan)',
'-DESCARGOT_DEBUGGER=<(escargot_debugger)',
'-DCMAKE_POLICY_VERSION_MINIMUM=3.5',
],
},
'all_dependent_settings': {
Expand Down
47 changes: 35 additions & 12 deletions include/lwnode/lwnode-public.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,34 +35,57 @@ LWNODE_EXPORT bool ParseAULEvent(int argc, char** argv);
* Sets the path of the root directory of the JavaScript. If you do
* not put the path argument, the root path is the app's resource path by
* default on Tizen AUL mode. Be sure to call this function before lwnode::Start
* function.
* function.
**/
LWNODE_EXPORT bool InitScriptRootPath(const std::string path = "");

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

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

class LWNODE_EXPORT Runtime {
public:
using SendMessageSyncCallback = std::string (*)(const std::string&,
void* user_data);

class Configuration {
public:
friend Runtime;
Configuration();
~Configuration();

Configuration(Configuration&) = delete;
Configuration(Configuration&&) = delete;
Configuration& operator=(const Configuration& t) = delete;
Configuration& operator=(Configuration&&);

void OnSendMessageSync(SendMessageSyncCallback callback, void* user_data);

private:
struct Internal;
Internal* internal_ = nullptr;
};

Runtime();
Runtime(Configuration&& config);

~Runtime();

/**
* Start the runtime and returns the exit code. It initializes the runtime
* and runs it. When the runtime is initialized, the promise object is set.
*
* @param argc - Argument count.
* @param argv - Argument vector. The element should be the starting file
* name of the application.
* @param promise - Promise object. It will be set when the runtime
* initialization is complete.
* @return Returns the exit code of the runtime.
**/
* Start the runtime and returns the exit code. It initializes the runtime
* and runs it. When the runtime is initialized, the promise object is set.
*
* @param argc - Argument count.
* @param argv - Argument vector. The element should be the starting file
* name of the application.
* @param promise - Promise object. It will be set when the runtime
* initialization is complete.
* @return Returns the exit code of the runtime.
**/
int Start(int argc, char** argv, std::promise<void>&& promise);

std::shared_ptr<Port> GetPort();
Expand Down
2 changes: 2 additions & 0 deletions include/lwnode/lwnode.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ enum ContextEmbedderIndex {
// Others are listed in deps/node/src/node_context_data.h.
kMainMessagePort = 90,
kLoopHolder = 91,
kSendMessageSyncCallback = 92,
kSendMessageSyncCallbackData = 93,
};

void InitializeProcessMethods(v8::Local<v8::Object> target,
Expand Down
31 changes: 31 additions & 0 deletions src/lwnode/lwnode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "api/utils/misc.h"
#include "api/utils/smaps.h"
#include "base.h"
#include "lwnode-public.h"
#include "lwnode/lwnode-gc-strategy.h"
#include "lwnode/lwnode-loader.h"

Expand Down Expand Up @@ -240,6 +241,33 @@ static ValueRef* Unref(ExecutionStateRef* state,
return ValueRef::create(loop_holder->ref_count());
}

static ValueRef* SendMessageSync(ExecutionStateRef* state,
ValueRef* this_value,
size_t argc,
ValueRef** argv,
bool isConstructCall) {
std::string message;
if (argc > 0 && argv[0]->isString()) {
message = argv[0]->asString()->toStdUTF8String();
}

ContextWrap* lwContext = ContextWrap::fromEscargot(state->context());
lwnode::Runtime::SendMessageSyncCallback callback =
reinterpret_cast<lwnode::Runtime::SendMessageSyncCallback>(
lwContext->GetAlignedPointerFromEmbedderData(
kSendMessageSyncCallback));
if (!callback) {
return ValueRef::createUndefined();
}

Comment thread
daeyeon marked this conversation as resolved.
void* data = lwContext->GetAlignedPointerFromEmbedderData(
kSendMessageSyncCallbackData);

std::string response = callback(message, data);

return StringRef::createFromUTF8(response.c_str(), response.length());
}

void InitMainMessagePort(Local<Context> context,
MainMessagePort* main_port,
LoopHolderUV* loop_holder,
Expand Down Expand Up @@ -277,6 +305,9 @@ void InitializeProcessMethods(Local<Object> target, Local<Context> context) {

SetMethod(esContext, esTarget, "ref", Ref);
SetMethod(esContext, esTarget, "unref", Unref);

SetMethod(esContext, esTarget, "sendMessageSync", SendMessageSync);

ModuleMessagePortInit(esContext, esTarget);
}

Expand Down
1 change: 1 addition & 0 deletions test/embedding/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ endif()

add_executable(embedtest.x embedtest.cc)
add_executable(example.x example.cc)
add_executable(send-message-sync.x send-message-sync.cc)
71 changes: 71 additions & 0 deletions test/embedding/send-message-sync.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include <lwnode-public.h>
#include <message-port.h>
#include <filesystem>
#include <future>
#include <iostream>
#include <memory>
#include <thread>

#define COUNT_OF(array) (sizeof(array) / sizeof((array)[0]))

class Info {
public:
Info(std::string name, std::string age, std::string gender)
: name_(name), age_(age), gender_(gender) {}

std::string GetName() const { return name_; }
std::string GetAge() const { return age_; }
std::string GetGender() const { return gender_; }

private:
std::string name_;
std::string age_;
std::string gender_;
};

int main(int argc, char* argv[]) {
std::shared_ptr<Info> info = std::make_shared<Info>("John", "30", "male");

lwnode::Runtime::Configuration configuration;
configuration.OnSendMessageSync(
[](const std::string& message, void* user_data) -> std::string {
Info* info = static_cast<Info*>(user_data);

if (message == "name") return info->GetName();
if (message == "age") return info->GetAge();
if (message == "gender") return info->GetGender();

return "";
},
(void*)info.get());

auto runtime = std::make_shared<lwnode::Runtime>(std::move(configuration));

std::promise<void> promise;
std::future<void> init_future = promise.get_future();
const char* script = "test/embedding/test-10-send-message-sync-basic.js";
std::string path = (std::filesystem::current_path() / script).string();
char* args[] = {const_cast<char*>(""), const_cast<char*>(path.c_str())};

std::thread worker = std::thread(
[&](std::promise<void>&& promise) mutable {
// FIXME: Fix Runtime::Init() call to ensure environment initialization
// before running the loop, Runtime::Run(). This workaround passes a
// promise directly to know when that is.
runtime->Start(COUNT_OF(args), args, std::move(promise));
},
std::move(promise));

init_future.wait();

int count1 = 0;
auto port2 = runtime->GetPort();
port2->OnMessage([&](const MessageEvent* event) {
std::cout << event->data() << std::endl;
count1++;
});
port2->PostMessage(MessageEvent::New("ping"));

worker.join();
return 0;
}
29 changes: 29 additions & 0 deletions test/embedding/test-10-send-message-sync-basic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const lwnode = process.lwnode;
const port = process.lwnode.port;

port.onmessage = (event) => {
console.log(`${event.data}`);
if (event.data == "ping") {
port.postMessage("pong");
}
};

function printMessage() {
console.log("printMessage called--------------------------------------------");
const name = lwnode.sendMessageSync('name');
console.log(`Hello, ${name}!`);

const age = lwnode.sendMessageSync('age');
console.log(`I am ${age} years old.`);

const gender = lwnode.sendMessageSync('gender');
console.log(`My gender is ${gender}.`);
}

let count = 10;
let loop = setInterval(() => {
if (count-- <= 0) {
clearInterval(loop);
}
printMessage();
}, 1000);
Loading