Skip to content

Commit

Permalink
Prototype Duktape 2.0 require system polyfill
Browse files Browse the repository at this point in the history
  • Loading branch information
creationix committed Oct 29, 2014
1 parent d126b3f commit 638d8fe
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 7 deletions.
2 changes: 2 additions & 0 deletions b.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

return 42;
1 change: 0 additions & 1 deletion modules/classes.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use strict";

exports.Emitter = Emitter;
function Emitter() {}
Emitter.prototype.on = function on(name, callback) {
Expand Down
105 changes: 101 additions & 4 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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[]) {
Expand Down
4 changes: 2 additions & 2 deletions tcp-echo.js
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
59 changes: 59 additions & 0 deletions test-require.js
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 638d8fe

Please sign in to comment.