Browse Source

Merge pull request #639 from svaarala/module-wrapper-explicit-filename

Add support for module.fileName and module.name
pull/649/head
Sami Vaarala 9 years ago
parent
commit
9aa47520fc
  1. 11
      RELEASES.rst
  2. 16
      doc/modules.rst
  3. 212
      src/duk_bi_global.c
  4. 87
      tests/ecmascript/test-commonjs-module-filename.js
  5. 16
      tests/ecmascript/test-commonjs-require-filename.js
  6. 38
      tests/ecmascript/test-commonjs-require-subrequire-name.js
  7. 3
      website/guide/modules.html

11
RELEASES.rst

@ -1396,6 +1396,17 @@ Planned
explicit filename is known; this makes file/line information thrown from
such code more useful in practice (GH-516, GH-644)
* Add support for non-standard module.fileName (initialized to resolved
module ID) and module.name (initialized to last component of resolved
module ID) which are used for the internal module wrapper function's
.fileName and .name properties; they show up in stack traces, debugger
integration, logger instance default naming, etc, and can now be
controlled by modSearch() (GH-639)
* Add a .name property for the require() functions created for included
modules, so that they have a readable name in stack traces like the top
level require() function (GH-639)
* Add Windows version of the debugger example TCP transport (GH-579)
* Add support for application specific debugger commands (AppRequest) and

16
doc/modules.rst

@ -12,7 +12,8 @@ built into Duktape:
- http://wiki.commonjs.org/wiki/Modules/1.1.1
- Duktape supports also ``module.exports``
- Duktape supports also ``module.exports`` and a few Duktape specific
properties (``module.fileName`` and ``module.name``)
* The user must provide a *module search function* which locates a module
corresponding to a resolved module ID, and can register module symbols
@ -252,6 +253,19 @@ Duktape supports ``module.exports`` since Duktape 1.3, see:
* ``test-commonjs-module-exports-repl.js``
module.fileName and module.name
===============================
The ``module.fileName`` and ``module.name`` properties are Duktape specific
and allow modSearch() to control the ``.fileName`` and ``.name`` properties
of the module wrapper function used to implement module loading. This is
useful because they appear in e.g. tracebacks for errors created from the
module, see: https://github.com/svaarala/duktape/pull/639.
The initial value for ``module.fileName`` is the full resolved module ID
(e.g. ``foo/bar``) and for ``module.name`` the last component of the
resolved module ID (e.g. ``bar``).
C modules and DLLs
==================

212
src/duk_bi_global.c

@ -580,7 +580,7 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_eval(duk_context *ctx) {
DUK_DDD(DUK_DDDPRINT("eval -> lex_env=%!iO, var_env=%!iO, this_binding=%!T",
(duk_heaphdr *) outer_lex_env,
(duk_heaphdr *) outer_var_env,
(duk_tval *) duk_get_tval(ctx, -1)));
duk_get_tval(ctx, -1)));
/* [ source template closure this ] */
@ -846,6 +846,7 @@ DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *re
duk_uint8_t buf_out[DUK_BI_COMMONJS_MODULE_ID_LIMIT];
duk_uint8_t *p;
duk_uint8_t *q;
duk_uint8_t *q_last; /* last component */
DUK_ASSERT(req_id != NULL);
/* mod_id may be NULL */
@ -907,10 +908,18 @@ DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *re
for (;;) {
duk_uint_fast8_t c;
/* Here 'p' always points to the start of a term. */
/* Here 'p' always points to the start of a term.
*
* We can also unconditionally reset q_last here: if this is
* the last (non-empty) term q_last will have the right value
* on loop exit.
*/
DUK_DDD(DUK_DDDPRINT("resolve loop top: p -> '%s', q=%p, buf_out=%p",
(const char *) p, (void *) q, (void *) buf_out));
q_last = q;
c = *p++;
if (DUK_UNLIKELY(c == 0)) {
DUK_DD(DUK_DDPRINT("resolve error: requested ID must end with a non-empty term"));
@ -959,6 +968,9 @@ DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *re
*q++ = c;
c = *p++;
if (DUK_UNLIKELY(c == 0)) {
/* This was the last term, and q_last was
* updated to match this term at loop top.
*/
goto loop_done;
} else if (DUK_UNLIKELY(c == '/')) {
*q++ = '/';
@ -980,8 +992,17 @@ DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *re
}
}
loop_done:
/* Output #1: resolved absolute name */
DUK_ASSERT(q >= buf_out);
duk_push_lstring(ctx, (const char *) buf_out, (size_t) (q - buf_out));
/* Output #2: last component name */
DUK_ASSERT(q >= q_last);
DUK_ASSERT(q_last >= buf_out);
duk_push_lstring(ctx, (const char *) q_last, (size_t) (q - q_last));
DUK_DD(DUK_DDPRINT("after resolving module name: buf_out=%p, q_last=%p, q=%p",
(void *) buf_out, (void *) q_last, (void *) q));
return;
resolve_error:
@ -990,6 +1011,19 @@ DUK_LOCAL void duk__bi_global_resolve_module_id(duk_context *ctx, const char *re
#endif /* DUK_USE_COMMONJS_MODULES */
#if defined(DUK_USE_COMMONJS_MODULES)
/* Stack indices for better readability */
#define DUK__IDX_REQUESTED_ID 0 /* Module id requested */
#define DUK__IDX_REQUIRE 1 /* Current require() function */
#define DUK__IDX_REQUIRE_ID 2 /* The base ID of the current require() function, resolution base */
#define DUK__IDX_RESOLVED_ID 3 /* Resolved, normalized absolute module ID */
#define DUK__IDX_LASTCOMP 4 /* Last component name in resolved path */
#define DUK__IDX_DUKTAPE 5 /* Duktape object */
#define DUK__IDX_MODLOADED 6 /* Duktape.modLoaded[] module cache */
#define DUK__IDX_UNDEFINED 7 /* 'undefined', artifact of lookup */
#define DUK__IDX_FRESH_REQUIRE 8 /* New require() function for module, updated resolution base */
#define DUK__IDX_EXPORTS 9 /* Default exports table */
#define DUK__IDX_MODULE 10 /* Module object containing module.exports, etc */
DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
const char *str_req_id; /* requested identifier */
const char *str_mod_id; /* require.id of current module */
@ -1005,23 +1039,24 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
* Resolve module identifier into canonical absolute form.
*/
str_req_id = duk_require_string(ctx, 0);
str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID);
duk_push_current_function(ctx);
duk_get_prop_stridx(ctx, -1, DUK_STRIDX_ID);
str_mod_id = duk_get_string(ctx, 2); /* ignore non-strings */
str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */
DUK_DDD(DUK_DDDPRINT("resolve module id: requested=%!T, currentmodule=%!T",
(duk_tval *) duk_get_tval(ctx, 0),
(duk_tval *) duk_get_tval(ctx, 2)));
duk_get_tval(ctx, DUK__IDX_REQUESTED_ID),
duk_get_tval(ctx, DUK__IDX_REQUIRE_ID)));
duk__bi_global_resolve_module_id(ctx, str_req_id, str_mod_id);
str_req_id = NULL;
str_mod_id = NULL;
DUK_DDD(DUK_DDDPRINT("resolved module id: requested=%!T, currentmodule=%!T, result=%!T",
(duk_tval *) duk_get_tval(ctx, 0),
(duk_tval *) duk_get_tval(ctx, 2),
(duk_tval *) duk_get_tval(ctx, 3)));
DUK_DDD(DUK_DDDPRINT("resolved module id: requested=%!T, currentmodule=%!T, result=%!T, lastcomp=%!T",
duk_get_tval(ctx, DUK__IDX_REQUESTED_ID),
duk_get_tval(ctx, DUK__IDX_REQUIRE_ID),
duk_get_tval(ctx, DUK__IDX_RESOLVED_ID),
duk_get_tval(ctx, DUK__IDX_LASTCOMP)));
/* [ requested_id require require.id resolved_id ] */
DUK_ASSERT_TOP(ctx, 4);
/* [ requested_id require require.id resolved_id last_comp ] */
DUK_ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1);
/*
* Cached module check.
@ -1032,27 +1067,22 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
* be supported to some extent.
*/
/* [ requested_id require require.id resolved_id ] */
DUK_ASSERT_TOP(ctx, 4);
duk_push_hobject_bidx(ctx, DUK_BIDX_DUKTAPE);
duk_get_prop_stridx(ctx, 4, DUK_STRIDX_MOD_LOADED); /* Duktape.modLoaded */
(void) duk_require_hobject(ctx, 5);
/* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded ] */
DUK_ASSERT_TOP(ctx, 6);
duk_get_prop_stridx(ctx, DUK__IDX_DUKTAPE, DUK_STRIDX_MOD_LOADED); /* Duktape.modLoaded */
(void) duk_require_hobject(ctx, DUK__IDX_MODLOADED);
DUK_ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1);
duk_dup(ctx, 3);
if (duk_get_prop(ctx, 5)) {
/* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
if (duk_get_prop(ctx, DUK__IDX_MODLOADED)) {
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded Duktape.modLoaded[id] ] */
DUK_DD(DUK_DDPRINT("module already loaded: %!T",
(duk_tval *) duk_get_tval(ctx, 3)));
duk_get_tval(ctx, DUK__IDX_RESOLVED_ID)));
duk_get_prop_stridx(ctx, -1, DUK_STRIDX_EXPORTS); /* return module.exports */
return 1;
}
DUK_ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1);
/* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined ] */
DUK_ASSERT_TOP(ctx, 7);
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */
/*
* Module not loaded (and loading not started previously).
@ -1064,8 +1094,12 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
* succeeds in finding the module.
*/
DUK_DD(DUK_DDPRINT("module not yet loaded: %!T",
(duk_tval *) duk_get_tval(ctx, 3)));
DUK_D(DUK_DPRINT("loading module %!T, resolution base %!T, requested ID %!T -> resolved ID %!T, last component %!T",
duk_get_tval(ctx, DUK__IDX_RESOLVED_ID),
duk_get_tval(ctx, DUK__IDX_REQUIRE_ID),
duk_get_tval(ctx, DUK__IDX_REQUESTED_ID),
duk_get_tval(ctx, DUK__IDX_RESOLVED_ID),
duk_get_tval(ctx, DUK__IDX_LASTCOMP)));
/* Fresh require: require.id is left configurable (but not writable)
* so that is not easy to accidentally tweak it, but it can still be
@ -1075,32 +1109,44 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
* is no practical reason to touch it.
*/
duk_push_c_function(ctx, duk_bi_global_object_require, 1 /*nargs*/);
duk_dup(ctx, 3);
duk_xdef_prop_stridx(ctx, 7, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_C); /* a fresh require() with require.id = resolved target module id */
duk_push_hstring_stridx(ctx, DUK_STRIDX_REQUIRE);
duk_xdef_prop_stridx(ctx, DUK__IDX_FRESH_REQUIRE, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_xdef_prop_stridx(ctx, DUK__IDX_FRESH_REQUIRE, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_C); /* a fresh require() with require.id = resolved target module id */
/* Module table:
* - module.exports: initial exports table (may be replaced by user)
* - module.id is non-writable and non-configurable, as the CommonJS
* spec suggests this if possible.
* spec suggests this if possible
* - module.fileName: fileName where module loaded from, defaults to
* resolved ID
* - module.name: name for module wrapper function, defaults to last
* component of resolved ID
*/
duk_push_object(ctx); /* exports */
duk_push_object(ctx); /* module */
duk_dup(ctx, -2);
duk_xdef_prop_stridx(ctx, 9, DUK_STRIDX_EXPORTS, DUK_PROPDESC_FLAGS_WC); /* module.exports = exports */
duk_dup(ctx, 3); /* resolved id: require(id) must return this same module */
duk_xdef_prop_stridx(ctx, 9, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_NONE); /* module.id = resolved_id */
duk_compact(ctx, 9); /* module table remains registered to modLoaded, minimize its size */
duk_dup(ctx, DUK__IDX_EXPORTS);
duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS, DUK_PROPDESC_FLAGS_WC); /* module.exports = exports */
duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */
duk_dup_top(ctx);
duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_ID, DUK_PROPDESC_FLAGS_NONE); /* module.id = resolved_id */
duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC); /* module.fileName = resolved_id */
duk_dup(ctx, DUK__IDX_LASTCOMP);
duk_xdef_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_WC); /* module.name = last_comp */
duk_compact(ctx, DUK__IDX_MODULE); /* module table remains registered to modLoaded, minimize its size */
DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 1);
/* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module ] */
DUK_ASSERT_TOP(ctx, 10);
DUK_DD(DUK_DDPRINT("module table created: %!T", duk_get_tval(ctx, DUK__IDX_MODULE)));
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module ] */
/* Register the module table early to modLoaded[] so that we can
* support circular references even in modSearch(). If an error
* is thrown, we'll delete the reference.
*/
duk_dup(ctx, 3);
duk_dup(ctx, 9);
duk_put_prop(ctx, 5); /* Duktape.modLoaded[resolved_id] = module */
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_dup(ctx, DUK__IDX_MODULE);
duk_put_prop(ctx, DUK__IDX_MODLOADED); /* Duktape.modLoaded[resolved_id] = module */
/*
* Call user provided module search function and build the wrapped
@ -1122,13 +1168,13 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
duk_push_string(ctx, "(function(require,exports,module){");
/* Duktape.modSearch(resolved_id, fresh_require, exports, module). */
duk_get_prop_stridx(ctx, 4, DUK_STRIDX_MOD_SEARCH); /* Duktape.modSearch */
duk_dup(ctx, 3);
duk_dup(ctx, 7);
duk_dup(ctx, 8);
duk_dup(ctx, 9); /* [ ... Duktape.modSearch resolved_id fresh_require exports module ] */
duk_get_prop_stridx(ctx, DUK__IDX_DUKTAPE, DUK_STRIDX_MOD_SEARCH); /* Duktape.modSearch */
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE);
duk_dup(ctx, DUK__IDX_EXPORTS);
duk_dup(ctx, DUK__IDX_MODULE); /* [ ... Duktape.modSearch resolved_id last_comp fresh_require exports module ] */
pcall_rc = duk_pcall(ctx, 4 /*nargs*/); /* -> [ ... source ] */
DUK_ASSERT_TOP(ctx, 12);
DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 3);
if (pcall_rc != DUK_EXEC_SUCCESS) {
/* Delete entry in Duktape.modLoaded[] and rethrow. */
@ -1138,7 +1184,7 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
/* If user callback did not return source code, module loading
* is finished (user callback initialized exports table directly).
*/
if (!duk_is_string(ctx, 11)) {
if (!duk_is_string(ctx, -1)) {
/* User callback did not return source code, so module loading
* is finished: just update modLoaded with final module.exports
* and we're done.
@ -1146,26 +1192,30 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
goto return_exports;
}
/* Finish the wrapped module source. Force resolved module ID as the
* fileName so it gets set for functions defined within a module. This
* also ensures loggers created within the module get the module ID as
* their default logger name.
/* Finish the wrapped module source. Force module.fileName as the
* function .fileName so it gets set for functions defined within a
* module. This also ensures loggers created within the module get
* the module ID (or overridden filename) as their default logger name.
*/
duk_push_string(ctx, "})");
duk_concat(ctx, 3);
duk_dup(ctx, 3); /* resolved module ID for fileName */
duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_FILE_NAME); /* module.fileName for fileName */
duk_eval_raw(ctx, NULL, 0, DUK_COMPILE_EVAL);
/* XXX: The module wrapper function is currently anonymous and is shown
* in stack traces. It would be nice to force it to match the module
* name (perhaps just the cleaned up last term). At the moment 'name'
* is write protected so we can't change it directly. Note that we must
* not introduce an actual name binding into the function scope (which
* is usually the case with a named function) because it would affect
* the scope seen by the module and shadow accesses to globals of the
* same name.
/* Module has now evaluated to a wrapped module function. Force its
* .name to match module.name (defaults to last component of resolved
* ID) so that it is shown in stack traces too. Note that we must not
* introduce an actual name binding into the function scope (which is
* usually the case with a named function) because it would affect the
* scope seen by the module and shadow accesses to globals of the same name.
* This is now done by compiling the function as anonymous and then forcing
* its .name without setting a "has name binding" flag.
*/
duk_push_hstring_stridx(ctx, DUK_STRIDX_NAME);
duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_NAME);
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE);
/*
* Call the wrapped module function.
*
@ -1173,16 +1223,16 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
* even if the module throws an error.
*/
/* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */
DUK_ASSERT_TOP(ctx, 11);
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func ] */
DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
duk_dup(ctx, 8); /* exports (this binding) */
duk_dup(ctx, 7); /* fresh require (argument) */
duk_get_prop_stridx(ctx, 9, DUK_STRIDX_EXPORTS); /* relookup exports from module.exports in case it was changed by modSearch */
duk_dup(ctx, 9); /* module (argument) */
duk_dup(ctx, DUK__IDX_EXPORTS); /* exports (this binding) */
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */
duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS); /* relookup exports from module.exports in case it was changed by modSearch */
duk_dup(ctx, DUK__IDX_MODULE); /* module (argument) */
DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 6);
/* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */
DUK_ASSERT_TOP(ctx, 15);
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module mod_func exports fresh_require exports module ] */
pcall_rc = duk_pcall_method(ctx, 3 /*nargs*/);
if (pcall_rc != DUK_EXEC_SUCCESS) {
@ -1193,21 +1243,33 @@ DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
goto delete_rethrow;
}
/* [ requested_id require require.id resolved_id Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */
DUK_ASSERT_TOP(ctx, 11);
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined fresh_require exports module result(ignored) ] */
DUK_ASSERT_TOP(ctx, DUK__IDX_MODULE + 2);
/* fall through */
return_exports:
duk_get_prop_stridx(ctx, 9, DUK_STRIDX_EXPORTS);
duk_get_prop_stridx(ctx, DUK__IDX_MODULE, DUK_STRIDX_EXPORTS);
return 1; /* return module.exports */
delete_rethrow:
duk_dup(ctx, 3);
duk_del_prop(ctx, 5); /* delete Duktape.modLoaded[resolved_id] */
duk_dup(ctx, DUK__IDX_RESOLVED_ID);
duk_del_prop(ctx, DUK__IDX_MODLOADED); /* delete Duktape.modLoaded[resolved_id] */
duk_throw(ctx); /* rethrow original error */
return 0; /* not reachable */
}
#undef DUK__IDX_REQUESTED_ID
#undef DUK__IDX_REQUIRE
#undef DUK__IDX_REQUIRE_ID
#undef DUK__IDX_RESOLVED_ID
#undef DUK__IDX_LASTCOMP
#undef DUK__IDX_DUKTAPE
#undef DUK__IDX_MODLOADED
#undef DUK__IDX_UNDEFINED
#undef DUK__IDX_FRESH_REQUIRE
#undef DUK__IDX_EXPORTS
#undef DUK__IDX_MODULE
#else
DUK_INTERNAL duk_ret_t duk_bi_global_object_require(duk_context *ctx) {
DUK_UNREF(ctx);

87
tests/ecmascript/test-commonjs-module-filename.js

@ -0,0 +1,87 @@
/*
* Duktape 1.5 added module.fileName and module.name support.
*/
/*===
default behavior
test/foo2 test/foo2 test/foo2 foo2
test/foo2
2
foo2
TIME INF test/foo2: test
override .name and .fileName
test/bar2 test/bar2 test/bar2 bar2
my_source.js
2
my_module
TIME INF my_source.js: test
.name shadowing test
test/quux2 test/quux2 test/quux2 quux2
object
number
123
===*/
var testSource =
'/* test */\n' +
'var err = new Error("aiee");\n' +
'print(err.fileName);\n' +
'print(err.lineNumber);\n' +
'print(Duktape.act(-2).function.name);\n' +
'/*print(err.stack);*/\n' +
'var logger = new Duktape.Logger();\n' +
'logger.info("test");\n';
function test() {
// Replace Duktape.Logger.prototype.raw to censor timestamps.
Duktape.Logger.prototype.raw = function (buf) {
print(String(buf).replace(/^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d.*?Z/, 'TIME'));
};
// Default behavior, module.fileName is resolved module ID.
Duktape.modSearch = function modSearch(id, require, exports, module) {
print(id, module.id, module.fileName, module.name);
return testSource;
};
print('default behavior');
var tmp = require('test/foo1/../foo2');
// Override module.fileName and module.name in modSearch().
Duktape.modSearch = function modSearch(id, require, exports, module) {
print(id, module.id, module.fileName, module.name);
module.fileName = 'my_source.js'; // match source filename for example
module.name = 'my_module';
return testSource;
};
print('override .name and .fileName');
var tmp = require('test/bar1/../bar2');
// Test that the forced .name property of the module wrapper function
// does not introduce a shadowing binding -- this is important for
// semantics and works because the wrapper function is initially
// compiled as an anonymous function (which ensures the function doesn't
// get a "has a name binding" flag) and .name is then forced manually.
var global = new Function('return this')();
global.myFileName = 123; // this should be visible
Duktape.modSearch = function modSearch(id, require, exports, module) {
print(id, module.id, module.fileName, module.name);
module.fileName = 'myFileName';
return 'print(typeof Math); print(typeof myFileName); print(myFileName);';
};
print('.name shadowing test');
var tmp = require('test/quux1/../quux2');
}
try {
test();
} catch (e) {
print(e.stack || e);
}

16
tests/ecmascript/test-commonjs-require-filename.js

@ -2,8 +2,14 @@
* Filename for functions inside a module loaded using require()
*
* In Duktape 1.0.0 this would always be "duk_bi_global.c" which is confusing.
* For Duktape 1.1.0 this was fixed to be the fully resolved module ID.
*
* In Duktape 1.1.0 this was fixed to be the fully resolved module ID.
* See GH-58 for discussion.
*
* In Duktape 1.5.0 the module wrapper function is also given a .name which
* defaults to the last component of the resolved module ID. Both the .name
* and .fileName can be overridden by modSearch via module.name and
* module.fileName.
*/
/*---
@ -13,10 +19,10 @@
---*/
/*===
moduleFunc name:
moduleFunc fileName: foo
moduleFunc name: foo
moduleFunc fileName: my/foo
testFunc name: testFunc
testFunc fileName: foo
testFunc fileName: my/foo
===*/
function modSearch() {
@ -35,7 +41,7 @@ function test() {
*/
Duktape.modSearch = modSearch;
var mod = require('foo');
var mod = require('my/foo');
/* However, functions defined within the module don't have a proper
* fileName in Duktape 1.0.0.

38
tests/ecmascript/test-commonjs-require-subrequire-name.js

@ -0,0 +1,38 @@
/*
* The fresh require() functions given to modules should have a .name so that
* they appear nicely in tracebacks. This was not the case in Duktape 1.4.x,
* fixed in Duktape 1.5.x.
*/
/*===
function
string
require
false false false
===*/
function test() {
Duktape.modSearch = function modSearch(id, require, exports, module) {
var pd;
print(typeof require);
print(typeof require.name);
print(require.name);
pd = Object.getOwnPropertyDescriptor(require, 'name');
print(pd.writable, pd.enumerable, pd.configurable);
//print(new Error('aiee').stack);
//require('../../../../foo')
return undefined;
};
require('foo/bar');
}
try {
test();
} catch (e) {
print(e.stack || e);
}

3
website/guide/modules.html

@ -3,7 +3,8 @@
<p>Duktape has a built-in minimal module loading framework based on
<a href="http://wiki.commonjs.org/wiki/Modules/1.1.1">CommonJS modules version 1.1.1</a>,
with additional support for <code>module.exports</code>.
with additional support for <code>module.exports</code> and a few Duktape
specific <code>module</code> object properties.
The internals are documented in
<a href="https://github.com/svaarala/duktape/blob/master/doc/modules.rst">modules.rst</a>.
A recommended (but not mandatory) C module convention is described in

Loading…
Cancel
Save