mirror of https://github.com/svaarala/duktape.git
Sami Vaarala
9 years ago
5 changed files with 576 additions and 0 deletions
@ -0,0 +1,13 @@ |
|||
# For manual testing; say 'make' in extras/module-duktape and run ./test.
|
|||
# There's test coverage in tests/ecmascript, so tests here are very simple.
|
|||
|
|||
.PHONY: test |
|||
test: |
|||
gcc -std=c99 -Wall -Wextra -o $@ -I../../src/ -I. ../../src/duktape.c duk_module_duktape.c test.c -lm |
|||
@printf '\n' |
|||
./test 'assert(typeof require === "function");' |
|||
./test 'assert(require.name === "require");' |
|||
./test 'assert(typeof Duktape.modLoaded === "object");' |
|||
./test 'assert(typeof Duktape.modSearch === "undefined");' |
|||
./test 'Duktape.modSearch = function myModSearch(id) { return "exports.foo = 123;" }; assert(require("dummy").foo === 123);' |
|||
./test 'Duktape.modSearch = function myModSearch(id) { return "exports.foo = 234;" }; delete Duktape; assert(typeof Duktape === "undefined"); assert(require("dummy").foo === 234);' |
@ -0,0 +1,31 @@ |
|||
=============================================== |
|||
Duktape 1.x compatible module loading framework |
|||
=============================================== |
|||
|
|||
The default built-in module loading framework was removed in Duktape 2.x |
|||
because more flexibility was needed for module loading. This directory |
|||
contains a Duktape 1.x compatible module loading framework which you can |
|||
add to your build: |
|||
|
|||
* Add ``duk_module_duktape.c`` to list of C sources to compile. |
|||
|
|||
* Ensure ``duk_module_duktape.h`` is in the include path. |
|||
|
|||
* Include the extra header in calling code and initialize the bindings:: |
|||
|
|||
#include "duktape.h" |
|||
#include "duk_module_duktape.h" |
|||
|
|||
/* After initializing the Duktape heap or when creating a new |
|||
* thread with a new global environment: |
|||
*/ |
|||
duk_module_duktape_init(ctx); |
|||
|
|||
Don't call ``duk_module_duktape_init()`` more than once for the same global |
|||
environment. |
|||
|
|||
* As usual in Duktape 1.x, you should define ``Duktape.modSearch()`` to provide |
|||
environment specific module lookups. |
|||
|
|||
* After these steps, ``require()`` will be registered to the global object and |
|||
the module system is ready to use. |
@ -0,0 +1,463 @@ |
|||
/*
|
|||
* Duktape 1.x compatible module loading framework |
|||
*/ |
|||
|
|||
#include "duktape.h" |
|||
#include "duk_module_duktape.h" |
|||
|
|||
#if 0 /* Enable manually */
|
|||
#define DUK__ASSERT(x) do { \ |
|||
if (!(x)) { \ |
|||
fprintf(stderr, "ASSERTION FAILED at %s:%d: " #x "\n", __FILE__, __LINE__); \ |
|||
fflush(stderr); \ |
|||
} \ |
|||
} while (0) |
|||
#define DUK__ASSERT_TOP(ctx,val) do { \ |
|||
DUK__ASSERT(duk_get_top((ctx)) == (val)); \ |
|||
} while (0) |
|||
#else |
|||
#define DUK__ASSERT(x) do { (void) (x); } while (0) |
|||
#define DUK__ASSERT_TOP(ctx,val) do { (void) ctx; (void) (val); } while (0) |
|||
#endif |
|||
|
|||
static void duk__resolve_module_id(duk_context *ctx, const char *req_id, const char *mod_id) { |
|||
duk_uint8_t buf[DUK_COMMONJS_MODULE_ID_LIMIT]; |
|||
duk_uint8_t *p; |
|||
duk_uint8_t *q; |
|||
duk_uint8_t *q_last; /* last component */ |
|||
duk_int_t int_rc; |
|||
|
|||
DUK__ASSERT(req_id != NULL); |
|||
/* mod_id may be NULL */ |
|||
|
|||
/*
|
|||
* A few notes on the algorithm: |
|||
* |
|||
* - Terms are not allowed to begin with a period unless the term |
|||
* is either '.' or '..'. This simplifies implementation (and |
|||
* is within CommonJS modules specification). |
|||
* |
|||
* - There are few output bound checks here. This is on purpose: |
|||
* the resolution input is length checked and the output is never |
|||
* longer than the input. The resolved output is written directly |
|||
* over the input because it's never longer than the input at any |
|||
* point in the algorithm. |
|||
* |
|||
* - Non-ASCII characters are processed as individual bytes and |
|||
* need no special treatment. However, U+0000 terminates the |
|||
* algorithm; this is not an issue because U+0000 is not a |
|||
* desirable term character anyway. |
|||
*/ |
|||
|
|||
/*
|
|||
* Set up the resolution input which is the requested ID directly |
|||
* (if absolute or no current module path) or with current module |
|||
* ID prepended (if relative and current module path exists). |
|||
* |
|||
* Suppose current module is 'foo/bar' and relative path is './quux'. |
|||
* The 'bar' component must be replaced so the initial input here is |
|||
* 'foo/bar/.././quux'. |
|||
*/ |
|||
|
|||
if (mod_id != NULL && req_id[0] == '.') { |
|||
int_rc = snprintf((char *) buf, sizeof(buf), "%s/../%s", mod_id, req_id); |
|||
} else { |
|||
int_rc = snprintf((char *) buf, sizeof(buf), "%s", req_id); |
|||
} |
|||
if (int_rc >= (duk_int_t) sizeof(buf) || int_rc < 0) { |
|||
/* Potentially truncated, NUL not guaranteed in any case.
|
|||
* The (int_rc < 0) case should not occur in practice. |
|||
*/ |
|||
goto resolve_error; |
|||
} |
|||
DUK__ASSERT(strlen((const char *) buf) < sizeof(buf)); /* at most sizeof(buf) - 1 */ |
|||
|
|||
/*
|
|||
* Resolution loop. At the top of the loop we're expecting a valid |
|||
* term: '.', '..', or a non-empty identifier not starting with a period. |
|||
*/ |
|||
|
|||
p = buf; |
|||
q = buf; |
|||
for (;;) { |
|||
duk_uint_fast8_t c; |
|||
|
|||
/* 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__ASSERT(p >= q); /* output is never longer than input during resolution */ |
|||
|
|||
q_last = q; |
|||
|
|||
c = *p++; |
|||
if (c == 0) { |
|||
goto resolve_error; |
|||
} else if (c == '.') { |
|||
c = *p++; |
|||
if (c == '/') { |
|||
/* Term was '.' and is eaten entirely (including dup slashes). */ |
|||
goto eat_dup_slashes; |
|||
} |
|||
if (c == '.' && *p == '/') { |
|||
/* Term was '..', backtrack resolved name by one component.
|
|||
* q[-1] = previous slash (or beyond start of buffer) |
|||
* q[-2] = last char of previous component (or beyond start of buffer) |
|||
*/ |
|||
p++; /* eat (first) input slash */ |
|||
DUK__ASSERT(q >= buf); |
|||
if (q == buf) { |
|||
goto resolve_error; |
|||
} |
|||
DUK__ASSERT(*(q - 1) == '/'); |
|||
q--; /* Backtrack to last output slash (dups already eliminated). */ |
|||
for (;;) { |
|||
/* Backtrack to previous slash or start of buffer. */ |
|||
DUK__ASSERT(q >= buf); |
|||
if (q == buf) { |
|||
break; |
|||
} |
|||
if (*(q - 1) == '/') { |
|||
break; |
|||
} |
|||
q--; |
|||
} |
|||
goto eat_dup_slashes; |
|||
} |
|||
goto resolve_error; |
|||
} else if (c == '/') { |
|||
/* e.g. require('/foo'), empty terms not allowed */ |
|||
goto resolve_error; |
|||
} else { |
|||
for (;;) { |
|||
/* Copy term name until end or '/'. */ |
|||
*q++ = c; |
|||
c = *p++; |
|||
if (c == 0) { |
|||
/* This was the last term, and q_last was
|
|||
* updated to match this term at loop top. |
|||
*/ |
|||
goto loop_done; |
|||
} else if (c == '/') { |
|||
*q++ = '/'; |
|||
break; |
|||
} else { |
|||
/* write on next loop */ |
|||
} |
|||
} |
|||
} |
|||
|
|||
eat_dup_slashes: |
|||
for (;;) { |
|||
/* eat dup slashes */ |
|||
c = *p; |
|||
if (c != '/') { |
|||
break; |
|||
} |
|||
p++; |
|||
} |
|||
} |
|||
loop_done: |
|||
/* Output #1: resolved absolute name. */ |
|||
DUK__ASSERT(q >= buf); |
|||
duk_push_lstring(ctx, (const char *) buf, (size_t) (q - buf)); |
|||
|
|||
/* Output #2: last component name. */ |
|||
DUK__ASSERT(q >= q_last); |
|||
DUK__ASSERT(q_last >= buf); |
|||
duk_push_lstring(ctx, (const char *) q_last, (size_t) (q - q_last)); |
|||
return; |
|||
|
|||
resolve_error: |
|||
duk_error(ctx, DUK_ERR_TYPE_ERROR, "cannot resolve module id: %s", (const char *) req_id); |
|||
} |
|||
|
|||
/* 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 */ |
|||
|
|||
static duk_ret_t duk__require(duk_context *ctx) { |
|||
const char *str_req_id; /* requested identifier */ |
|||
const char *str_mod_id; /* require.id of current module */ |
|||
duk_int_t pcall_rc; |
|||
|
|||
/* NOTE: we try to minimize code size by avoiding unnecessary pops,
|
|||
* so the stack looks a bit cluttered in this function. DUK__ASSERT_TOP() |
|||
* assertions are used to ensure stack configuration is correct at each |
|||
* step. |
|||
*/ |
|||
|
|||
/*
|
|||
* Resolve module identifier into canonical absolute form. |
|||
*/ |
|||
|
|||
str_req_id = duk_require_string(ctx, DUK__IDX_REQUESTED_ID); |
|||
duk_push_current_function(ctx); |
|||
duk_get_prop_string(ctx, -1, "id"); |
|||
str_mod_id = duk_get_string(ctx, DUK__IDX_REQUIRE_ID); /* ignore non-strings */ |
|||
duk__resolve_module_id(ctx, str_req_id, str_mod_id); |
|||
str_req_id = NULL; |
|||
str_mod_id = NULL; |
|||
|
|||
/* [ requested_id require require.id resolved_id last_comp ] */ |
|||
DUK__ASSERT_TOP(ctx, DUK__IDX_LASTCOMP + 1); |
|||
|
|||
/*
|
|||
* Cached module check. |
|||
* |
|||
* If module has been loaded or its loading has already begun without |
|||
* finishing, return the same cached value (module.exports). The |
|||
* value is registered when module load starts so that circular |
|||
* references can be supported to some extent. |
|||
*/ |
|||
|
|||
duk_push_global_stash(ctx); |
|||
duk_get_prop_string(ctx, -1, "\xff" "module:Duktape"); |
|||
duk_remove(ctx, -2); /* Lookup stashed, original 'Duktape' object. */ |
|||
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modLoaded"); /* Duktape.modLoaded */ |
|||
duk_require_type_mask(ctx, DUK__IDX_MODLOADED, DUK_TYPE_MASK_OBJECT); |
|||
DUK__ASSERT_TOP(ctx, DUK__IDX_MODLOADED + 1); |
|||
|
|||
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_get_prop_string(ctx, -1, "exports"); /* return module.exports */ |
|||
return 1; |
|||
} |
|||
DUK__ASSERT_TOP(ctx, DUK__IDX_UNDEFINED + 1); |
|||
|
|||
/* [ requested_id require require.id resolved_id last_comp Duktape Duktape.modLoaded undefined ] */ |
|||
|
|||
/*
|
|||
* Module not loaded (and loading not started previously). |
|||
* |
|||
* Create a new require() function with 'id' set to resolved ID |
|||
* of module being loaded. Also create 'exports' and 'module' |
|||
* tables but don't register exports to the loaded table yet. |
|||
* We don't want to do that unless the user module search callbacks |
|||
* succeeds in finding the module. |
|||
*/ |
|||
|
|||
/* Fresh require: require.id is left configurable (but not writable)
|
|||
* so that is not easy to accidentally tweak it, but it can still be |
|||
* done with Object.defineProperty(). |
|||
* |
|||
* XXX: require.id could also be just made non-configurable, as there |
|||
* is no practical reason to touch it (at least from Ecmascript code). |
|||
*/ |
|||
duk_push_c_function(ctx, duk__require, 1 /*nargs*/); |
|||
duk_push_string(ctx, "name"); |
|||
duk_push_string(ctx, "require"); |
|||
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE); /* not writable, not enumerable, not configurable */ |
|||
duk_push_string(ctx, "id"); |
|||
duk_dup(ctx, DUK__IDX_RESOLVED_ID); |
|||
duk_def_prop(ctx, DUK__IDX_FRESH_REQUIRE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_CONFIGURABLE); /* 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 |
|||
* - module.filename: not set, defaults to resolved ID if not explicitly |
|||
* set by modSearch() (note capitalization, not .fileName, matches Node.js) |
|||
* - module.name: not set, defaults to last component of resolved ID if |
|||
* not explicitly set by modSearch() |
|||
*/ |
|||
duk_push_object(ctx); /* exports */ |
|||
duk_push_object(ctx); /* module */ |
|||
duk_push_string(ctx, "exports"); |
|||
duk_dup(ctx, DUK__IDX_EXPORTS); |
|||
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); /* module.exports = exports */ |
|||
duk_push_string(ctx, "id"); |
|||
duk_dup(ctx, DUK__IDX_RESOLVED_ID); /* resolved id: require(id) must return this same module */ |
|||
duk_def_prop(ctx, DUK__IDX_MODULE, DUK_DEFPROP_HAVE_VALUE); /* module.id = resolved_id; not writable, not enumerable, not configurable */ |
|||
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 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, 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 |
|||
* module source code (if necessary). The module search function |
|||
* can be used to implement pure Ecmacsript, pure C, and mixed |
|||
* Ecmascript/C modules. |
|||
* |
|||
* The module search function can operate on the exports table directly |
|||
* (e.g. DLL code can register values to it). It can also return a |
|||
* string which is interpreted as module source code (if a non-string |
|||
* is returned the module is assumed to be a pure C one). If a module |
|||
* cannot be found, an error must be thrown by the user callback. |
|||
* |
|||
* Because Duktape.modLoaded[] already contains the module being |
|||
* loaded, circular references for C modules should also work |
|||
* (although expected to be quite rare). |
|||
*/ |
|||
|
|||
duk_push_string(ctx, "(function(require,exports,module){"); |
|||
|
|||
/* Duktape.modSearch(resolved_id, fresh_require, exports, module). */ |
|||
duk_get_prop_string(ctx, DUK__IDX_DUKTAPE, "modSearch"); /* 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, DUK__IDX_MODULE + 3); |
|||
|
|||
if (pcall_rc != DUK_EXEC_SUCCESS) { |
|||
/* Delete entry in Duktape.modLoaded[] and rethrow. */ |
|||
goto delete_rethrow; |
|||
} |
|||
|
|||
/* If user callback did not return source code, module loading
|
|||
* is finished (user callback initialized exports table directly). |
|||
*/ |
|||
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. |
|||
*/ |
|||
goto return_exports; |
|||
} |
|||
|
|||
/* 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. |
|||
* (Note capitalization: .filename matches Node.js while .fileName is |
|||
* used elsewhere in Duktape.) |
|||
*/ |
|||
duk_push_string(ctx, "})"); |
|||
duk_concat(ctx, 3); |
|||
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "filename")) { |
|||
/* module.filename for .fileName, default to resolved ID if
|
|||
* not present. |
|||
*/ |
|||
duk_pop(ctx); |
|||
duk_dup(ctx, DUK__IDX_RESOLVED_ID); |
|||
} |
|||
pcall_rc = duk_pcompile(ctx, DUK_COMPILE_EVAL); |
|||
if (pcall_rc != DUK_EXEC_SUCCESS) { |
|||
goto delete_rethrow; |
|||
} |
|||
pcall_rc = duk_pcall(ctx, 0); /* -> eval'd function wrapper (not called yet) */ |
|||
if (pcall_rc != DUK_EXEC_SUCCESS) { |
|||
goto delete_rethrow; |
|||
} |
|||
|
|||
/* 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_string(ctx, "name"); |
|||
if (!duk_get_prop_string(ctx, DUK__IDX_MODULE, "name")) { |
|||
/* module.name for .name, default to last component if
|
|||
* not present. |
|||
*/ |
|||
duk_pop(ctx); |
|||
duk_dup(ctx, DUK__IDX_LASTCOMP); |
|||
} |
|||
duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); |
|||
|
|||
/*
|
|||
* Call the wrapped module function. |
|||
* |
|||
* Use a protected call so that we can update Duktape.modLoaded[resolved_id] |
|||
* even if the module throws an error. |
|||
*/ |
|||
|
|||
/* [ 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, DUK__IDX_EXPORTS); /* exports (this binding) */ |
|||
duk_dup(ctx, DUK__IDX_FRESH_REQUIRE); /* fresh require (argument) */ |
|||
duk_get_prop_string(ctx, DUK__IDX_MODULE, "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 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) { |
|||
/* Module loading failed. Node.js will forget the module
|
|||
* registration so that another require() will try to load |
|||
* the module again. Mimic that behavior. |
|||
*/ |
|||
goto delete_rethrow; |
|||
} |
|||
|
|||
/* [ 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_string(ctx, DUK__IDX_MODULE, "exports"); |
|||
duk_compact(ctx, -1); /* compact the exports table */ |
|||
return 1; /* return module.exports */ |
|||
|
|||
delete_rethrow: |
|||
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 */ |
|||
} |
|||
|
|||
void duk_module_duktape_init(duk_context *ctx) { |
|||
/* Stash 'Duktape' in case it's modified. */ |
|||
duk_push_global_stash(ctx); |
|||
duk_get_global_string(ctx, "Duktape"); |
|||
duk_put_prop_string(ctx, -2, "\xff" "module:Duktape"); |
|||
duk_pop(ctx); |
|||
|
|||
/* Register `require` as a global function. */ |
|||
duk_eval_string(ctx, |
|||
"(function(req){" |
|||
"var D=Object.defineProperty;" |
|||
"D(req,'name',{value:'require'});" |
|||
"D(this,'require',{value:req,writable:true,configurable:true});" |
|||
"D(Duktape,'modLoaded',{value:{},writable:true,configurable:true});" |
|||
"})"); |
|||
duk_push_c_function(ctx, duk__require, 1 /*nargs*/); |
|||
duk_call(ctx, 1); |
|||
duk_pop(ctx); |
|||
} |
|||
|
|||
#undef DUK__ASSERT |
|||
#undef DUK__ASSERT_TOP |
|||
#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 |
@ -0,0 +1,14 @@ |
|||
#if !defined(DUK_MODULE_DUKTAPE_H_INCLUDED) |
|||
#define DUK_MODULE_DUKTAPE_H_INCLUDED |
|||
|
|||
#include "duktape.h" |
|||
|
|||
/* Maximum length of CommonJS module identifier to resolve. Length includes
|
|||
* both current module ID, requested (possibly relative) module ID, and a |
|||
* slash in between. |
|||
*/ |
|||
#define DUK_COMMONJS_MODULE_ID_LIMIT 256 |
|||
|
|||
extern void duk_module_duktape_init(duk_context *ctx); |
|||
|
|||
#endif /* DUK_MODULE_DUKTAPE_H_INCLUDED */ |
@ -0,0 +1,55 @@ |
|||
#include <stdio.h> |
|||
#include <string.h> |
|||
#include "duktape.h" |
|||
#include "duk_module_duktape.h" |
|||
|
|||
static duk_ret_t handle_print(duk_context *ctx) { |
|||
printf("%s\n", duk_safe_to_string(ctx, 0)); |
|||
return 0; |
|||
} |
|||
|
|||
static duk_ret_t handle_assert(duk_context *ctx) { |
|||
if (duk_to_boolean(ctx, 0)) { |
|||
return 0; |
|||
} |
|||
duk_error(ctx, DUK_ERR_ERROR, "assertion failed: %s", duk_safe_to_string(ctx, 1)); |
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int main(int argc, char *argv[]) { |
|||
duk_context *ctx; |
|||
int i; |
|||
int exitcode = 0; |
|||
|
|||
ctx = duk_create_heap_default(); |
|||
if (!ctx) { |
|||
return 1; |
|||
} |
|||
|
|||
duk_push_c_function(ctx, handle_print, 1); |
|||
duk_put_global_string(ctx, "print"); |
|||
duk_push_c_function(ctx, handle_assert, 2); |
|||
duk_put_global_string(ctx, "assert"); |
|||
|
|||
duk_module_duktape_init(ctx); |
|||
printf("top after init: %ld\n", (long) duk_get_top(ctx)); |
|||
|
|||
for (i = 1; i < argc; i++) { |
|||
printf("Evaling: %s\n", argv[i]); |
|||
if (duk_peval_string(ctx, argv[i]) != 0) { |
|||
if (duk_get_prop_string(ctx, -1, "stack")) { |
|||
duk_replace(ctx, -2); |
|||
} else { |
|||
duk_pop(ctx); |
|||
} |
|||
exitcode = 1; |
|||
} |
|||
printf("--> %s\n", duk_safe_to_string(ctx, -1)); |
|||
duk_pop(ctx); |
|||
} |
|||
|
|||
printf("Done\n"); |
|||
duk_destroy_heap(ctx); |
|||
return exitcode; |
|||
} |
Loading…
Reference in new issue