From 638d8fe6debce34a60cede2c3f962a926e44b0f7 Mon Sep 17 00:00:00 2001 From: Tim Caswell Date: Wed, 29 Oct 2014 16:45:55 -0500 Subject: [PATCH] Prototype Duktape 2.0 require system polyfill --- b.js | 2 + modules/classes.js | 1 - src/main.c | 105 +++++++++++++++++++++++++++++++++++++++++++-- tcp-echo.js | 4 +- test-require.js | 59 +++++++++++++++++++++++++ 5 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 b.js create mode 100644 test-require.js diff --git a/b.js b/b.js new file mode 100644 index 0000000..77af079 --- /dev/null +++ b/b.js @@ -0,0 +1,2 @@ + +return 42; diff --git a/modules/classes.js b/modules/classes.js index 5c6fdc5..a02f880 100644 --- a/modules/classes.js +++ b/modules/classes.js @@ -1,5 +1,4 @@ "use strict"; - exports.Emitter = Emitter; function Emitter() {} Emitter.prototype.on = function on(name, callback) { diff --git a/src/main.c b/src/main.c index 088e502..cf91692 100644 --- a/src/main.c +++ b/src/main.c @@ -3,6 +3,100 @@ static uv_loop_t loop; +static duk_ret_t duv_require(duk_context *ctx) { + dschema_check(ctx, (const duv_schema_entry[]) { + {"id", duk_is_string}, + {NULL} + }); + + // push Duktape + duk_get_global_string(ctx, "Duktape"); + + // id = Duktape.modResolve(this, id); + duk_get_prop_string(ctx, -1, "modResolve"); + duk_push_this(ctx); + duk_dup(ctx, 0); + duk_call(ctx, 2); + duk_replace(ctx, 0); + + // push Duktape.modLoaded + duk_get_prop_string(ctx, -1, "modLoaded"); + + // push Duktape.modLoaded[id]; + duk_dup(ctx, 0); + duk_get_prop(ctx, -2); + + // if (typeof Duktape.modLoaded[id] === 'object') { + // return Duktape.modLoaded[id].exports; + // } + if (duk_is_object(ctx, -1)) { + duk_get_prop_string(ctx, -1, "exports"); + return 1; + } + + // pop Duktape.modLoaded[id] + duk_pop(ctx); + + // push module = { id: id, exports: {} } + duk_push_object(ctx); + duk_dup(ctx, 0); + duk_put_prop_string(ctx, -2, "id"); + duk_push_object(ctx); + duk_put_prop_string(ctx, -2, "exports"); + + // Duktape.modLoaded[id] = module + duk_dup(ctx, 0); + duk_dup(ctx, -2); + duk_put_prop(ctx, -4); + + // remove Duktape.modLoaded + duk_remove(ctx, -2); + + // push Duktape.modLoad(module) + duk_get_prop_string(ctx, -2, "modLoad"); + duk_dup(ctx, -2); + duk_call(ctx, 1); + + // if ret !== undefined module.exports = ret; + if (duk_is_undefined(ctx, -1)) { + duk_pop(ctx); + } + else { + duk_put_prop_string(ctx, -2, "exports"); + } + + duk_get_prop_string(ctx, -1, "exports"); + + return 1; +} + +// Given a module and js code, compile the code and execute as CJS module +// return the result of the compiled code ran as a function. +static duk_ret_t duv_mod_compile(duk_context *ctx) { + // Check the args + dschema_check(ctx, (const duv_schema_entry[]) { + {"module", duk_is_object}, + {"code", dschema_is_data}, + {NULL} + }); + duk_to_string(ctx, 1); + + // Wrap the code + duk_push_string(ctx, "function(require,module){require=require.bind(module);var exports=module.exports;"); + duk_dup(ctx, 1); + duk_push_string(ctx, "}"); + duk_concat(ctx, 3); + + // Compile to a function + duk_get_prop_string(ctx, 0, "id"); + duk_compile(ctx, DUK_COMPILE_FUNCTION); + duk_push_c_function(ctx, duv_require, 1); + duk_dup(ctx, 0); + duk_call(ctx, 2); + + return 1; +} + // Sync readfile using libuv APIs as an API function. static duk_ret_t duv_loadfile(duk_context *ctx) { const char* path = duk_require_string(ctx, 0); @@ -49,17 +143,20 @@ static duk_ret_t duv_main(duk_context *ctx) { duk_call(ctx, 0); duk_put_prop_string(ctx, -2, "http_parser"); - // Setup the module loader + // Replace the module loader with Duktape 2.x polyfill. duk_get_prop_string(ctx, -1, "Duktape"); - duk_push_c_function(ctx, duv_loadfile, 1); - duk_put_prop_string(ctx, -2, "modSearch"); + duk_del_prop_string(ctx, -1, "modSearch"); + duk_push_c_function(ctx, duv_mod_compile, 2); + duk_put_prop_string(ctx, -2, "modCompile"); duk_pop(ctx); + duk_push_c_function(ctx, duv_require, 1); + duk_put_prop_string(ctx, -2, "newRequire"); + duk_eval_file_noresult(ctx, path); uv_run(&loop, UV_RUN_DEFAULT); return 0; - } int main(int argc, char *argv[]) { diff --git a/tcp-echo.js b/tcp-echo.js index 92433e9..fcf660a 100644 --- a/tcp-echo.js +++ b/tcp-echo.js @@ -1,6 +1,6 @@ "use strict"; -var p = require('modules/utils.js').prettyPrint; -var Tcp = require('modules/classes.js').Tcp; +var p = require('./modules/utils.js').prettyPrint; +var Tcp = require('./modules/classes.js').Tcp; var server, socket, client; diff --git a/test-require.js b/test-require.js new file mode 100644 index 0000000..a5ae3a5 --- /dev/null +++ b/test-require.js @@ -0,0 +1,59 @@ +"use strict"; + +// Replace Duktape 1.x require with polyfilled 2.x require +// This won't be needed when new require is native. +var module = {id:uv.cwd()+"/test-require.js",exports:{}}; +require = newRequire.bind(module); + +// Set up our app-specefic require hooks. +Duktape.modResolve = function (parent, id) { + if (id[0] !== ".") { return id; } + return pathJoin(parent.id, "..", id); +}; + +Duktape.modLoad = function (module) { + var code = loadFile(module.id); + return Duktape.modCompile(module, code); +}; + +// Test require +var p = require('./modules/utils.js').prettyPrint; +p(require('./b.js')); + +require('./tcp-echo.js'); + +function loadFile(path) { + var fd = uv.fs_open(path, "r", 420 /*0644*/); + try { + var stat = uv.fs_fstat(fd); + var chunk = uv.fs_read(fd, stat.size, 0); + return chunk; + } + finally { + uv.fs_close(fd); + } +} + +function pathJoin() { + var parts = [].join.call(arguments, "/").split("/").filter(Boolean); + var skip = 0; + for (var i = parts.length - 1; i >= 0; --i) { + var part = parts[i]; + if (part === '.') { + parts.splice(i, 1); + } + else if (part === '..') { + parts.splice(i, 1); + ++skip; + } + else if (skip) { + parts.splice(i, 1); + --skip; + } + } + var path = parts.join("/"); + if (arguments[0][0] === "/") { + path = "/" + path; + } + return path; +}