Skip to content

Commit c5418a1

Browse files
committed
Basic module implementation
1 parent 92f97a7 commit c5418a1

File tree

3 files changed

+117
-5
lines changed

3 files changed

+117
-5
lines changed

‎Makefile‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
LD_FLAGS+= -L/home/john/repos/v8/v8/out.gn/x64.release.sample/obj/ -lv8_monolith -lpthread
2-
CXX_FLAGS+= -fvisibility=hidden -fPIC -O2 -g -isystem /home/john/repos/v8/v8/include -DV8_COMPRESS_POINTERS
1+
LD_FLAGS+= -L/home/john/repos/v8/v8/out.gn/x64.release.sample/obj/ -lv8_monolith -lpthread -lstdc++fs
2+
CXX_FLAGS+= -std=c++17 -fvisibility=hidden -fPIC -O0 -g -isystem /home/john/repos/v8/v8/include -DV8_COMPRESS_POINTERS
33

44
MODULE_OBJS = js.o module.o
55

‎js.cpp‎

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
#include <libplatform/libplatform.h>
44
#include <fstream>
55
#include <streambuf>
6+
#include <stack>
7+
#include <experimental/filesystem>
68

79
void KeyDBExecuteCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
810

911
thread_local v8::Isolate *isolate = nullptr;
1012
thread_local v8::Persistent<v8::ObjectTemplate, v8::CopyablePersistentTraits<v8::ObjectTemplate>> tls_global;
1113

12-
1314
void javascript_initialize()
1415
{
1516
v8::V8::InitializeICUDefaultLocation("keydb-server");
@@ -41,6 +42,108 @@ static void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
4142
printf("%s\n", *value);
4243
}
4344

45+
template<typename T>
46+
class StackPopper
47+
{
48+
std::stack<T> &m_stack;
49+
public:
50+
StackPopper(std::stack<T> &stack, T &&val)
51+
: m_stack(stack)
52+
{
53+
stack.push(val);
54+
}
55+
56+
~StackPopper()
57+
{
58+
m_stack.pop();
59+
}
60+
};
61+
62+
static void RequireCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
63+
{
64+
static thread_local std::stack<std::experimental::filesystem::path> stackpath;
65+
66+
if (args.Length() != 1) return;
67+
v8::HandleScope scope(isolate);
68+
v8::Local<v8::Value> arg = args[0];
69+
v8::String::Utf8Value utf8Path(isolate, arg);
70+
71+
std::experimental::filesystem::path path(*utf8Path);
72+
73+
if (path.is_relative() && !stackpath.empty())
74+
{
75+
// We have to make this relative to the previous file
76+
path = stackpath.top().remove_filename() / path;
77+
}
78+
printf("Loading module: %s\n", path.c_str());
79+
80+
81+
std::ifstream file(path.c_str(), std::ios::binary | std::ios::ate);
82+
std::streamsize size = file.tellg();
83+
if (size == -1)
84+
{
85+
isolate->ThrowException(v8::String::NewFromUtf8(isolate, "File not found").ToLocalChecked());
86+
return; // Failed to read file
87+
}
88+
89+
file.seekg(0, std::ios::beg);
90+
StackPopper popper(stackpath, std::move(path));
91+
92+
std::vector<char> buffer(size);
93+
if (!file.read(buffer.data(), size))
94+
{
95+
isolate->ThrowException(v8::String::NewFromUtf8(isolate, "File not found").ToLocalChecked());
96+
return; // Failed to read file
97+
}
98+
99+
v8::Local<v8::ObjectTemplate> global = v8::Local<v8::ObjectTemplate>::New(isolate, tls_global);
100+
v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
101+
102+
v8::ScriptOrigin origin(arg, // specifier
103+
v8::Integer::New(isolate, 0), // line offset
104+
v8::Integer::New(isolate, 0), // column offset
105+
v8::False(isolate), // is cross origin
106+
v8::Local<v8::Integer>(), // script id
107+
v8::Local<v8::Value>(), // source map URL
108+
v8::False(isolate), // is opaque
109+
v8::False(isolate), // is WASM
110+
v8::True(isolate)); // is ES6 module
111+
112+
v8::Context::Scope context_scope(context);
113+
114+
v8::Local<v8::String> source_text =
115+
v8::String::NewFromUtf8(isolate, buffer.data(),
116+
v8::NewStringType::kNormal, buffer.size())
117+
.ToLocalChecked();
118+
119+
v8::ScriptCompiler::Source source(source_text, origin);
120+
121+
v8::Local<v8::Module> module;
122+
if (!v8::ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
123+
return;
124+
}
125+
126+
bool flagT;
127+
auto maybeInstantiated = module->InstantiateModule(context, [](v8::Local<v8::Context> context, // "main.mjs"
128+
v8::Local<v8::String> specifier, // "some thing"
129+
v8::Local<v8::Module> referrer) -> v8::MaybeLocal<v8::Module> {
130+
return v8::Local<v8::Module>();
131+
});
132+
if (!maybeInstantiated.To(&flagT) || !flagT)
133+
{
134+
return;
135+
}
136+
137+
v8::Local<v8::Value> result;
138+
if (module->Evaluate(context).ToLocal(&result)) {
139+
v8::Local<v8::Object> valglob = context->Global();
140+
auto maybeExports = valglob->Get(context, v8::String::NewFromUtf8(isolate, "exports").ToLocalChecked());
141+
v8::Local<v8::Value> exports;
142+
if (maybeExports.ToLocal(&exports))
143+
args.GetReturnValue().Set(exports);
144+
}
145+
}
146+
44147

45148
void javascript_hooks_initialize(v8::Local<v8::ObjectTemplate> &keydb_obj)
46149
{
@@ -74,6 +177,12 @@ void javascript_thread_initialize()
74177
global->Set(v8::String::NewFromUtf8(isolate, "redis", v8::NewStringType::kNormal)
75178
.ToLocalChecked(),
76179
keydb_obj);
180+
global->Set(v8::String::NewFromUtf8(isolate, "require", v8::NewStringType::kNormal)
181+
.ToLocalChecked(),
182+
v8::FunctionTemplate::New(isolate, RequireCallback));
183+
global->Set(v8::String::NewFromUtf8(isolate, "exports", v8::NewStringType::kNormal)
184+
.ToLocalChecked(),
185+
v8::ObjectTemplate::New(isolate));
77186

78187
tls_global = v8::Persistent<v8::ObjectTemplate, v8::CopyablePersistentTraits<v8::ObjectTemplate>>(isolate, global);
79188
}
@@ -138,7 +247,6 @@ v8::Local<v8::Value> javascript_run(v8::Local<v8::Context> &context, const char
138247
return result;
139248
}
140249

141-
142250
void javascript_thread_shutdown()
143251
{
144252
isolate->Dispose();

‎module.cpp‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,15 @@ static void processResult(RedisModuleCtx *ctx, v8::Local<v8::Context> &v8ctx, v8
118118
double dv = num->Value();
119119
RedisModule_ReplyWithDouble(ctx, dv);
120120
}
121-
else
121+
else if (result->IsString())
122122
{
123123
v8::String::Utf8Value utf8(isolate, result);
124124
RedisModule_ReplyWithCString(ctx, *utf8);
125125
}
126+
else
127+
{
128+
RedisModule_ReplyWithNull(ctx);
129+
}
126130
}
127131

128132
int evaljs_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)

0 commit comments

Comments
 (0)