Skip to content

Commit 91de834

Browse files
committed
Create JS standard lib
1 parent 93777b7 commit 91de834

File tree

4 files changed

+154
-48
lines changed

4 files changed

+154
-48
lines changed

bootstrap.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
var keydb = new Object();
3+
keydb.commands = new Object();
4+
5+
keydb.modjs_version = _internal.version();
6+
7+
keydb.call = function(...args)
8+
{
9+
return _internal.call(...args);
10+
}
11+
12+
keydb.register = function(fn, flags = "write deny-oom random", keyFirst = 0, keyLast = 0, keyStep = 0)
13+
{
14+
this.commands += fn;
15+
return _internal.register(fn, flags, keyFirst, keyLast, keyStep);
16+
}
17+
18+
keydb.log = function()
19+
{
20+
if (arguments.length == 1) {
21+
_internal.log("notice", arguments[0]);
22+
} else if (arguments.length == 2) {
23+
_internal.log(arguments[0], arguments[1]);
24+
} else {
25+
_internal.log("warning", "log() called with invalid arguments");
26+
}
27+
}
28+
29+
var console = {log: keydb.log} // alias keydb.log to console.log
30+
var redis = keydb; // Alias

js.cpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
#include <stack>
77
#include <experimental/filesystem>
88
#include "sha256.h"
9+
#include "version.h"
910

1011
void KeyDBExecuteCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
1112
void RegisterCommandCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
13+
void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
1214

1315
void javascript_initialize()
1416
{
@@ -67,16 +69,6 @@ class HotScript
6769
}
6870
};
6971

70-
static void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
71-
{
72-
if (args.Length() < 1) return;
73-
v8::Isolate* isolate = args.GetIsolate();
74-
v8::HandleScope scope(isolate);
75-
v8::Local<v8::Value> arg = args[0];
76-
v8::String::Utf8Value value(isolate, arg);
77-
printf("%s\n", *value);
78-
}
79-
8072
template<typename T>
8173
class StackPopper
8274
{
@@ -234,6 +226,14 @@ std::experimental::filesystem::path find_module(std::experimental::filesystem::p
234226
}
235227

236228

229+
static void VersionCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
230+
{
231+
v8::Isolate *isolate = args.GetIsolate();
232+
v8::HandleScope scope(isolate);
233+
auto strVersion = v8::String::NewFromUtf8(isolate, MODJS_VERSION).ToLocalChecked();
234+
args.GetReturnValue().Set(strVersion);
235+
}
236+
237237
void JSContext::javascript_hooks_initialize(v8::Local<v8::ObjectTemplate> &keydb_obj)
238238
{
239239
keydb_obj->Set(v8::String::NewFromUtf8(isolate, "log", v8::NewStringType::kNormal)
@@ -247,6 +247,10 @@ void JSContext::javascript_hooks_initialize(v8::Local<v8::ObjectTemplate> &keydb
247247
keydb_obj->Set(v8::String::NewFromUtf8(isolate, "register", v8::NewStringType::kNormal)
248248
.ToLocalChecked(),
249249
v8::FunctionTemplate::New(isolate, RegisterCommandCallback));
250+
251+
keydb_obj->Set(v8::String::NewFromUtf8(isolate, "version", v8::NewStringType::kNormal)
252+
.ToLocalChecked(),
253+
v8::FunctionTemplate::New(isolate, VersionCallback));
250254
}
251255

252256
void JSContext::initialize()
@@ -264,10 +268,7 @@ void JSContext::initialize()
264268

265269
javascript_hooks_initialize(keydb_obj);
266270

267-
global->Set(v8::String::NewFromUtf8(isolate, "keydb", v8::NewStringType::kNormal)
268-
.ToLocalChecked(),
269-
keydb_obj);
270-
global->Set(v8::String::NewFromUtf8(isolate, "redis", v8::NewStringType::kNormal)
271+
global->Set(v8::String::NewFromUtf8(isolate, "_internal", v8::NewStringType::kNormal)
271272
.ToLocalChecked(),
272273
keydb_obj);
273274
global->Set(v8::String::NewFromUtf8(isolate, "require", v8::NewStringType::kNormal)

module.cpp

Lines changed: 106 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,28 @@
66
#include <math.h>
77
#include <fstream>
88
#include <streambuf>
9+
#include <dlfcn.h>
10+
#include <experimental/filesystem>
911

1012
RedisModuleCtx *g_ctx = nullptr;
1113
JSContext *g_jscontext = nullptr;
14+
bool g_fInStartup = true;
15+
16+
class KeyDBContext
17+
{
18+
RedisModuleCtx *m_ctxSave;
19+
public:
20+
KeyDBContext(RedisModuleCtx *ctxSet)
21+
{
22+
m_ctxSave = g_ctx;
23+
g_ctx = ctxSet;
24+
}
25+
26+
~KeyDBContext()
27+
{
28+
g_ctx = m_ctxSave;
29+
}
30+
};
1231

1332
static void processResult(RedisModuleCtx *ctx, v8::Isolate *isolate, v8::Local<v8::Context> &v8ctx, v8::Local<v8::Value> &result)
1433
{
@@ -129,8 +148,27 @@ void KeyDBExecuteCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
129148
RedisModule_FreeString(g_ctx, str);
130149
}
131150

151+
void LogCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
152+
{
153+
v8::Isolate* isolate = args.GetIsolate();
154+
v8::HandleScope scope(isolate);
155+
156+
if (args.Length() != 2)
157+
{
158+
isolate->ThrowException(v8::String::NewFromUtf8(isolate, "Log expects two parameters").ToLocalChecked());
159+
return;
160+
}
161+
162+
163+
v8::String::Utf8Value level(isolate, args[0]);
164+
v8::String::Utf8Value message(isolate, args[1]);
165+
RedisModule_Log(g_ctx, *level, "%s", *message);
166+
}
167+
132168
int js_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
133169
{
170+
KeyDBContext ctxsav(ctx);
171+
134172
v8::Locker locker(g_jscontext->getIsolate());
135173
v8::HandleScope scope(g_jscontext->getIsolate());
136174
if (argc < 1)
@@ -169,6 +207,7 @@ int js_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
169207
}
170208

171209
auto maybeResult = fnCall->Call(context, global, (int)vecargs.size(), vecargs.data());
210+
172211
v8::Local<v8::Value> result;
173212
if (!maybeResult.ToLocal(&result))
174213
{
@@ -181,13 +220,11 @@ int js_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
181220
catch (std::string strerr)
182221
{
183222
RedisModule_ReplyWithError(ctx, strerr.c_str());
184-
g_ctx = nullptr;
185223
return REDISMODULE_ERR;
186224
}
187225
catch (std::nullptr_t)
188226
{
189227
RedisModule_ReplyWithError(ctx, "Unknown Error");
190-
g_ctx = nullptr;
191228
return REDISMODULE_ERR;
192229
}
193230
return REDISMODULE_OK;
@@ -199,6 +236,12 @@ void RegisterCommandCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
199236
v8::Isolate* isolate = args.GetIsolate();
200237
v8::HandleScope scope(isolate);
201238

239+
if (!g_fInStartup)
240+
{
241+
isolate->ThrowException(v8::String::NewFromUtf8(isolate, "New commands may only be registered during startup").ToLocalChecked());
242+
return;
243+
}
244+
202245
v8::Local<v8::Value> vfn = args[0];
203246
if (!vfn->IsFunction())
204247
return;
@@ -250,6 +293,8 @@ void RegisterCommandCallback(const v8::FunctionCallbackInfo<v8::Value>& args)
250293

251294
int evaljs_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
252295
{
296+
KeyDBContext ctxsav(ctx);
297+
253298
if (argc != 2)
254299
{
255300
RedisModule_WrongArity(ctx);
@@ -268,7 +313,6 @@ int evaljs_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
268313
const char *rgch = RedisModule_StringPtrLen(argv[1], &cch);
269314
try
270315
{
271-
g_ctx = ctx;
272316
v8::HandleScope scope(g_jscontext->getIsolate());
273317
v8::Local<v8::Value> result = g_jscontext->run(rgch, cch);
274318
auto context = g_jscontext->getCurrentContext();
@@ -277,16 +321,47 @@ int evaljs_command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
277321
catch (std::string strerr)
278322
{
279323
RedisModule_ReplyWithError(ctx, strerr.c_str());
280-
g_ctx = nullptr;
281324
return REDISMODULE_ERR;
282325
}
283326
catch (std::nullptr_t)
284327
{
285328
RedisModule_ReplyWithError(ctx, "Unknown Error");
286-
g_ctx = nullptr;
287329
return REDISMODULE_ERR;
288330
}
289-
g_ctx = nullptr;
331+
return REDISMODULE_OK;
332+
}
333+
334+
int run_startup_script(RedisModuleCtx *ctx, const char *rgchPath, size_t cchPath)
335+
{
336+
KeyDBContext ctxsav(ctx);
337+
338+
std::ifstream file(rgchPath, std::ios::binary | std::ios::ate);
339+
std::streamsize size = file.tellg();
340+
if (size == -1)
341+
{
342+
RedisModule_Log(ctx, "warning", "startup script does not exist");
343+
return REDISMODULE_ERR; // Failed to read file
344+
}
345+
346+
file.seekg(0, std::ios::beg);
347+
348+
std::vector<char> buffer(size);
349+
if (!file.read(buffer.data(), size))
350+
{
351+
RedisModule_Log(ctx, "warning", "failed to read startup script");
352+
return REDISMODULE_ERR; // Failed to read file
353+
}
354+
355+
v8::HandleScope scope(g_jscontext->getIsolate());
356+
try
357+
{
358+
g_jscontext->run(buffer.data(), buffer.size(), true /* don't cache */);
359+
}
360+
catch (std::string str)
361+
{
362+
RedisModule_Log(ctx, "warning", "%s", str.c_str());
363+
return REDISMODULE_ERR;
364+
}
290365
return REDISMODULE_OK;
291366
}
292367

@@ -304,42 +379,39 @@ extern "C" int __attribute__((visibility("default"))) RedisModule_OnLoad(RedisMo
304379
g_jscontext = new JSContext();
305380
g_jscontext->initialize();
306381

307-
if (argc >= 1)
382+
// Run our bootstrap.js code
308383
{
309-
// Process the startup script
310-
size_t cchPath;
311-
const char *rgchPath = RedisModule_StringPtrLen(argv[0], &cchPath);
312-
313-
std::ifstream file(rgchPath, std::ios::binary | std::ios::ate);
314-
std::streamsize size = file.tellg();
315-
if (size == -1)
384+
Dl_info dlInfo;
385+
dladdr((const void*)RedisModule_OnLoad, &dlInfo);
386+
if (dlInfo.dli_sname != NULL && dlInfo.dli_saddr != NULL)
316387
{
317-
RedisModule_Log(ctx, "warning", "startup script does not exist");
318-
return REDISMODULE_ERR; // Failed to read file
388+
std::experimental::filesystem::path path(dlInfo.dli_fname);
389+
path.remove_filename();
390+
path /= "bootstrap.js";
391+
std::string strPath = path.string();
392+
if (run_startup_script(ctx, strPath.data(), strPath.size()) == REDISMODULE_ERR)
393+
{
394+
RedisModule_Log(ctx, "warning", "failed to run bootstrap.js, ensure this is located in the same location as the .so");
395+
return REDISMODULE_ERR;
396+
}
319397
}
320-
321-
file.seekg(0, std::ios::beg);
322-
323-
std::vector<char> buffer(size);
324-
if (!file.read(buffer.data(), size))
398+
else
325399
{
326-
RedisModule_Log(ctx, "warning", "failed to read startup script");
327-
return REDISMODULE_ERR; // Failed to read file
400+
RedisModule_Log(ctx, "warning", "failed to locate bootstrap script");
401+
return REDISMODULE_ERR;
328402
}
403+
}
329404

330-
v8::HandleScope scope(g_jscontext->getIsolate());
331-
try
332-
{
333-
g_ctx = ctx;
334-
g_jscontext->run(buffer.data(), buffer.size(), true /* don't cache */);
335-
g_ctx = nullptr;
336-
}
337-
catch (std::string str)
338-
{
339-
RedisModule_Log(ctx, "warning", "%s", str.c_str());
405+
for (int iarg = 0; iarg < argc; ++iarg)
406+
{
407+
// Process the startup script
408+
size_t cchPath;
409+
const char *rgchPath = RedisModule_StringPtrLen(argv[iarg], &cchPath);
410+
411+
if (run_startup_script(ctx, rgchPath, cchPath) == REDISMODULE_ERR)
340412
return REDISMODULE_ERR;
341-
}
342413
}
343414

415+
g_fInStartup = false;
344416
return REDISMODULE_OK;
345417
}

version.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
const char *MODJS_VERSION = "1.0.0";

0 commit comments

Comments
 (0)