Skip to content

Commit c24fc5e

Browse files
committed
Loading node environment & functions from a file.
This has many problems which are discussed in TryGhost#448.
1 parent f8cd018 commit c24fc5e

File tree

4 files changed

+88
-50
lines changed

4 files changed

+88
-50
lines changed

src/database.cc

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void Database::Init(Handle<Object> target) {
2828
NODE_SET_PROTOTYPE_METHOD(t, "serialize", Serialize);
2929
NODE_SET_PROTOTYPE_METHOD(t, "parallelize", Parallelize);
3030
NODE_SET_PROTOTYPE_METHOD(t, "configure", Configure);
31-
NODE_SET_PROTOTYPE_METHOD(t, "registerFunction", RegisterFunction);
31+
NODE_SET_PROTOTYPE_METHOD(t, "loadEnvironment", RegisterFunctions);
3232

3333
NODE_SET_GETTER(t, "open", OpenGetter);
3434

@@ -361,15 +361,12 @@ NAN_METHOD(Database::Configure) {
361361
NanReturnValue(args.This());
362362
}
363363

364-
NAN_METHOD(Database::RegisterFunction) {
364+
NAN_METHOD(Database::RegisterFunctions) {
365365
NanScope();
366366
Database* db = ObjectWrap::Unwrap<Database>(args.This());
367367

368-
REQUIRE_ARGUMENTS(2);
369-
REQUIRE_ARGUMENT_STRING(0, functionName);
370-
REQUIRE_ARGUMENT_FUNCTION(1, callback);
371-
372-
std::string str = "(" + std::string(*String::Utf8Value(callback->ToString())) + ")";
368+
REQUIRE_ARGUMENTS(1);
369+
REQUIRE_ARGUMENT_STRING(0, module);
373370

374371
Isolate *isolate = v8::Isolate::New();
375372
isolate->Enter();
@@ -379,25 +376,32 @@ NAN_METHOD(Database::RegisterFunction) {
379376
HandleScope handle_scope(isolate);
380377
Local<Context> context = Context::New(isolate);
381378
Context::Scope context_scope(context);
379+
Environment *env = CreateEnvironment(isolate, uv_default_loop(), context,
380+
2, (const char *[]){ "node", *module },
381+
0, (const char *[]){});
382+
LoadEnvironment(env);
382383

383384
Local<Object> global = NanGetCurrentContext()->Global();
384-
Local<Function> eval = Local<Function>::Cast(global->Get(NanNew<String>("eval")));
385-
386-
// Local<String> str = String::Concat(String::Concat(NanNew<String>("("), callback->ToString()), NanNew<String>(")"));
387-
Local<Value> argv[] = { NanNew<String>(str.c_str(), str.length()) };
388-
// Local<Function> function = Local<Function>::Cast(TRY_CATCH_CALL(global, eval, 1, argv));
389-
Local<Function> function = Local<Function>::Cast(eval->Call(global, 1, argv));
390-
391-
FunctionEnvironment *fn = new FunctionEnvironment(isolate, *functionName, function);
392-
sqlite3_create_function(
393-
db->_handle,
394-
*functionName,
395-
-1, // arbitrary number of args
396-
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
397-
fn,
398-
FunctionIsolate,
399-
NULL,
400-
NULL);
385+
Local<Object> process = Local<Object>::Cast(global->Get(NanNew<String>("process")));
386+
Local<Object> mainModule = Local<Object>::Cast(process->Get(NanNew<String>("mainModule")));
387+
Local<Object> exports = Local<Object>::Cast(mainModule->Get(NanNew<String>("exports")));
388+
Local<Array> keys = exports->GetOwnPropertyNames();
389+
int length = keys->Length();
390+
for (int i = 0; i < length; i++) {
391+
Local<Function> function = Local<Function>::Cast(exports->Get(keys->Get(i)));
392+
String::Utf8Value functionName(keys->Get(i)->ToString());
393+
394+
FunctionEnvironment *fn = new FunctionEnvironment(isolate, *functionName, function);
395+
sqlite3_create_function(
396+
db->_handle,
397+
*functionName,
398+
-1, // arbitrary number of args
399+
SQLITE_UTF8 | SQLITE_DETERMINISTIC,
400+
fn,
401+
FunctionIsolate,
402+
NULL,
403+
NULL);
404+
}
401405
}
402406
isolate->Exit();
403407

@@ -461,7 +465,7 @@ void Database::FunctionExecute(FunctionEnvironment *fn, sqlite3_context *context
461465
}
462466

463467
TryCatch trycatch;
464-
Local<Value> result = cb->Call(NanGetCurrentContext()->Global(), argc, argv.data());
468+
Local<Value> result = cb->Call(NanNew(NanUndefined()), argc, argv.data());
465469

466470
// process the result
467471
if (trycatch.HasCaught()) {

src/database.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class Database : public ObjectWrap {
166166

167167
static NAN_METHOD(Configure);
168168

169-
static NAN_METHOD(RegisterFunction);
169+
static NAN_METHOD(RegisterFunctions);
170170
static void FunctionIsolate(sqlite3_context *context, int argc, sqlite3_value **argv);
171171
static void FunctionExecute(FunctionEnvironment *baton, sqlite3_context *context, int argc, sqlite3_value **argv);
172172

test/support/user_functions.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
exports.MY_UPPERCASE = function(value) {
2+
return value.toUpperCase();
3+
};
4+
5+
exports.MY_STRING_JOIN = function(value1, value2) {
6+
return [value1, value2].join(' ');
7+
};
8+
9+
exports.MY_Add = function(value1, value2) {
10+
return value1 + value2;
11+
};
12+
13+
exports.MY_REGEX = function(regex, value) {
14+
return !!value.match(new RegExp(regex));
15+
};
16+
17+
exports.MY_REGEX_VALUE = function(regex, value) {
18+
return /match things/i;
19+
};
20+
21+
exports.MY_ERROR = function(value) {
22+
throw new Error('This function always throws');
23+
};
24+
25+
exports.MY_UNHANDLED_TYPE = function(value) {
26+
return {};
27+
};
28+
29+
exports.MY_NOTHING = function(value) {
30+
31+
};
32+
33+
exports.MY_INVALID_SCOPING = function(value) {
34+
return db; // not accessible
35+
};
36+
37+
exports.MY_REQUIRE = function(value) {
38+
require('./helper');
39+
};

test/user_functions.test.js

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,13 @@
11
var sqlite3 = require('..');
22
var assert = require('assert');
3+
var path = require('path');
34

45
describe('user functions', function() {
56
var db;
67
before(function(done) { db = new sqlite3.Database(':memory:', done); });
78

89
it('should allow registration of user functions', function() {
9-
db.registerFunction('MY_UPPERCASE', function(value) {
10-
return value.toUpperCase();
11-
});
12-
db.registerFunction('MY_STRING_JOIN', function(value1, value2) {
13-
return [value1, value2].join(' ');
14-
});
15-
db.registerFunction('MY_Add', function(value1, value2) {
16-
return value1 + value2;
17-
});
18-
db.registerFunction('MY_REGEX', function(regex, value) {
19-
return !!value.match(new RegExp(regex));
20-
});
21-
db.registerFunction('MY_REGEX_VALUE', function(regex, value) {
22-
return /match things/i;
23-
});
24-
db.registerFunction('MY_ERROR', function(value) {
25-
throw new Error('This function always throws');
26-
});
27-
db.registerFunction('MY_UNHANDLED_TYPE', function(value) {
28-
return {};
29-
});
30-
db.registerFunction('MY_NOTHING', function(value) {
31-
32-
});
10+
db.loadEnvironment(path.join(__dirname, 'support/user_functions.js'));
3311
});
3412

3513
it('should process user functions with one arg', function(done) {
@@ -103,5 +81,22 @@ describe('user functions', function() {
10381
});
10482
});
10583

84+
it('does not allow access to external scope', function(done) {
85+
db.all('SELECT MY_INVALID_SCOPING() AS val', function(err, rows) {
86+
assert.equal(err.message, 'SQLITE_ERROR: Uncaught ReferenceError: db is not defined');
87+
assert.equal(rows, undefined);
88+
done();
89+
});
90+
});
91+
92+
it('allows use of require', function(done) {
93+
db.all('SELECT MY_REQUIRE() AS val', function(err, rows) {
94+
if (err) throw err;
95+
assert.equal(rows.length, 1);
96+
assert.equal(rows[0].val, undefined);
97+
done();
98+
});
99+
});
100+
106101
after(function(done) { db.close(done); });
107102
});

0 commit comments

Comments
 (0)