|
3 | 3 | #include <libplatform/libplatform.h> |
4 | 4 | #include <fstream> |
5 | 5 | #include <streambuf> |
| 6 | +#include <stack> |
| 7 | +#include <experimental/filesystem> |
6 | 8 |
|
7 | 9 | void KeyDBExecuteCallback(const v8::FunctionCallbackInfo<v8::Value>& args); |
8 | 10 |
|
9 | 11 | thread_local v8::Isolate *isolate = nullptr; |
10 | 12 | thread_local v8::Persistent<v8::ObjectTemplate, v8::CopyablePersistentTraits<v8::ObjectTemplate>> tls_global; |
11 | 13 |
|
12 | | - |
13 | 14 | void javascript_initialize() |
14 | 15 | { |
15 | 16 | v8::V8::InitializeICUDefaultLocation("keydb-server"); |
@@ -41,6 +42,108 @@ static void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args) |
41 | 42 | printf("%s\n", *value); |
42 | 43 | } |
43 | 44 |
|
| 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 | + |
44 | 147 |
|
45 | 148 | void javascript_hooks_initialize(v8::Local<v8::ObjectTemplate> &keydb_obj) |
46 | 149 | { |
@@ -74,6 +177,12 @@ void javascript_thread_initialize() |
74 | 177 | global->Set(v8::String::NewFromUtf8(isolate, "redis", v8::NewStringType::kNormal) |
75 | 178 | .ToLocalChecked(), |
76 | 179 | 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)); |
77 | 186 |
|
78 | 187 | tls_global = v8::Persistent<v8::ObjectTemplate, v8::CopyablePersistentTraits<v8::ObjectTemplate>>(isolate, global); |
79 | 188 | } |
@@ -138,7 +247,6 @@ v8::Local<v8::Value> javascript_run(v8::Local<v8::Context> &context, const char |
138 | 247 | return result; |
139 | 248 | } |
140 | 249 |
|
141 | | - |
142 | 250 | void javascript_thread_shutdown() |
143 | 251 | { |
144 | 252 | isolate->Dispose(); |
|
0 commit comments