diff --git a/duktape.cmake b/duktape.cmake index c5856ef..66d7f04 100644 --- a/duktape.cmake +++ b/duktape.cmake @@ -1,10 +1,26 @@ set(DUKTAPEDIR ${CMAKE_CURRENT_LIST_DIR}/lib/duktape) +if(EXISTS ${DUKTAPEDIR}/extras/logging/duk_logging.c) +# Duktape 2.x: assume both logging provider and duk-v1-compat exists. +# The duk-v1-compat transition helpers are needed for duk_put_function_list(). include_directories( ${DUKTAPEDIR}/src + ${DUKTAPEDIR}/extras/logging + ${DUKTAPEDIR}/extras/duk-v1-compat ) - -add_library(duktape STATIC ${DUKTAPEDIR}/src/duktape.c) +add_library(duktape STATIC + ${DUKTAPEDIR}/src/duktape.c + ${DUKTAPEDIR}/extras/logging/duk_logging.c + ${DUKTAPEDIR}/extras/duk-v1-compat/duk_v1_compat.c +) +else() +include_directories( + ${DUKTAPEDIR}/src +) +add_library(duktape STATIC + ${DUKTAPEDIR}/src/duktape.c +) +endif() if("${CMAKE_SYSTEM_NAME}" MATCHES "Linux") target_link_libraries(duktape diff --git a/modules/utils.js b/modules/utils.js index b78c7e9..04a7712 100644 --- a/modules/utils.js +++ b/modules/utils.js @@ -25,6 +25,21 @@ cbrace = colorize("braces", '}'); comma = colorize("sep", ','); colon = colorize("sep", ':'); +var bufferObjectTypes = { + Buffer: true, + ArrayBuffer: true, // Duktape 2.x: both plain buffer and ArrayBuffer + Uint8Array: true, + Uint8ClampedArray: true, + Int8Array: true, + Uint16Array: true, + Int16Array: true, + Uint32Array: true, + Int32Array: true, + Float32Array: true, + Float64Array: true, + DataView: true +}; + function color(color_name) { return "\x1b[" + (color_name ? theme[color_name] : "0") + "m"; } @@ -33,13 +48,19 @@ function colorize(color_name, string, reset_name) { return color(color_name) + string + color(reset_name); } -function dump(value) { +var getPlainBuffer = typeof ArrayBuffer.plainOf === 'function' ? + ArrayBuffer.plainOf : Duktape.Buffer; +var isPlainBuffer = function (v) { + return Duktape.info(v)[0] === 7; // Duktape 1.x and 2.x +}; +function dump(value) { var seen = []; return dumper(value, 0); + function dumper(value, depth) { + var i, n, t; var type = typeof value; - if (type === "undefined") { return colorize("undefined", "undefined"); } @@ -60,6 +81,8 @@ function dump(value) { }); } var info = Duktape.info(value); + var fullName = Object.prototype.toString.call(value); + var name = fullName.substring(8, fullName.length - 1); if (type === "function") { var fname = value.name || info[1]; // Native CFunctions don't have a .prototype property. @@ -68,24 +91,31 @@ function dump(value) { } return colorize("cfunction", "[Native " + fname + "]"); } - var fullName = Object.prototype.toString.call(value); - var name = fullName.substring(8, fullName.length - 1); if (name === "RegExp") { return colorize("regexp", "[RegExp " + value + "]"); } if (name === "Thread") { return colorize("thread", "[Thread " + info[1] + "]"); } - if (name === "Buffer") { - var preview = Array.prototype.slice.call(value, 0, 10).map(function (byte) { - return byte < 16 ? "0" + byte.toString(16) : byte.toString(16); - }).join(" "); - if (value.length > 10) { preview += "..."; } - // Fixed buffers have undefined for info[4] - if (info[4] === undefined) { - return colorize("buffer", "[Buffer " + preview + "]"); + if (type === "buffer" || // Duktape 1.x plain buffer + bufferObjectTypes[name]) { // Duktape 2.x plain buffer, ArrayBuffer, typed array, Node.js Buffer + var plain = getPlainBuffer(value); + var bytes = []; + for (i = 0, n = Math.min(value.byteLength, 10); i < n; i++) { + t = plain[value.byteOffset + i]; + bytes.push(t < 16 ? "0" + t.toString(16) : t.toString(16)); + } + var preview = bytes.join(" "); + if (value.byteLength > 10) { preview += "..."; } + if (isPlainBuffer(value)) { + if (info[4] === void 0) { + // Fixed buffers have undefined for info[4] + return colorize("buffer", "[Buffer " + preview + "]"); + } else { + return colorize("dbuffer", "[Dynamic Buffer " + preview + "]"); + } } - return colorize("dbuffer", "[Dynamic Buffer " + preview + "]"); + return colorize("buffer", "[" + name + " " + preview + "]"); } if (name === "Pointer") { return colorize("pointer", "[Pointer " + info[1] + "]"); diff --git a/repl.js b/repl.js index b8fd6d0..641030a 100644 --- a/repl.js +++ b/repl.js @@ -4,7 +4,7 @@ var utils = require('./modules/utils.js'); var p = utils.prettyPrint; function Timer() { - var obj = new Duktape.Buffer(uv.new_timer()); + var obj = Object(uv.new_timer()); // coerce to ArrayBuffer obj.__proto__ = Timer.prototype; return obj; } @@ -16,7 +16,11 @@ uv.read_start(utils.stdin, function (err, chunk) { if (err) { throw err; } if (!chunk) { return uv.read_stop(utils.stdin); } try { - p(eval(chunk.toString())); + if (Duktape.version >= 19999) { + p(eval(String.fromBuffer(chunk))); + } else { + p(eval(chunk.toString())); + } } catch (error) { uv.write(utils.stderr, utils.colorize("error", error.toString()) + "\n"); diff --git a/src/duv.h b/src/duv.h index cba9411..c7de45d 100644 --- a/src/duv.h +++ b/src/duv.h @@ -3,6 +3,9 @@ #include "uv.h" #include "duktape.h" +#if DUK_VERSION >= 19999 +#include "duk_v1_compat.h" +#endif #include #if !defined(_WIN32) diff --git a/src/main.c b/src/main.c index 7d4f8db..062d403 100644 --- a/src/main.c +++ b/src/main.c @@ -422,6 +422,13 @@ static duk_ret_t duv_main(duk_context *ctx) { duk_put_prop_string(ctx, -2, "loadlib"); duk_pop(ctx); +#if DUK_VERSION >= 19999 + /* Built-in module loader was removed in 2.0.0. Duktape.modLoaded[] + * was also removed and we depend on it, so add it if missing. + */ + duk_eval_string_noresult(ctx, "Object.defineProperty(Duktape, 'modLoaded', { value: {}, writable: true, enumerable: false, configurable: true });"); +#endif + // Put in some quick globals to test things. duk_push_c_function(ctx, duv_path_join, DUK_VARARGS); duk_put_prop_string(ctx, -2, "pathJoin"); @@ -454,11 +461,19 @@ static duk_ret_t duv_main(duk_context *ctx) { return 0; } +#if DUK_VERSION >= 19999 /* duk_safe_call() udata added in 2.0.0 (1.99.99 is pre-release) */ +static duk_ret_t duv_stash_argv(duk_context *ctx, void *udata) { +#else static duk_ret_t duv_stash_argv(duk_context *ctx) { +#endif char **argv = (char **) duk_require_pointer(ctx, 0); int argc = (int) duk_require_int(ctx, 1); int i; +#if DUK_VERSION >= 19999 /* duk_safe_call() udata added in 2.0.0 (1.99.99 is pre-release) */ + (void) udata; +#endif + duk_push_global_stash(ctx); duk_push_array(ctx); for (i = 0; i < argc; i++) { @@ -470,6 +485,37 @@ static duk_ret_t duv_stash_argv(duk_context *ctx) { return 0; } +#if DUK_VERSION >= 19999 +// Print/alert provider with Duktape 1.x semantics. +static duk_ret_t duv_print_alert_helper(duk_context *ctx, FILE *fh) { + duk_idx_t nargs, i; + const duk_uint8_t *buf; + duk_size_t sz_buf; + const char nl = (const char) '\n'; + + nargs = duk_get_top(ctx); + if (nargs == 1 && duk_is_buffer(ctx, 0)) { + buf = (const duk_uint8_t *) duk_get_buffer(ctx, 0, &sz_buf); + fwrite((const void *) buf, 1, (size_t) sz_buf, fh); + return 0; + } + /* Coerce all arguments before writing anything so that if there are + * side effects with print() calls, they are written first. + */ + duk_push_string(ctx, " "); + duk_insert(ctx, 0); + duk_join(ctx, nargs); + fprintf(fh, "%s\n", duk_to_string(ctx, -1)); + return 0; +} +static duk_ret_t duv_print(duk_context *ctx) { + return duv_print_alert_helper(ctx, stdout); +} +static duk_ret_t duv_alert(duk_context *ctx) { + return duv_print_alert_helper(ctx, stderr); +} +#endif + static void duv_dump_error(duk_context *ctx, duk_idx_t idx) { fprintf(stderr, "\nUncaught Exception:\n"); if (duk_is_object(ctx, idx)) { @@ -482,6 +528,22 @@ static void duv_dump_error(duk_context *ctx, duk_idx_t idx) { } } +#if DUK_VERSION >= 19999 +static void duv_fatal(void *udata, const char *msg) { + (void) udata; + fprintf(stderr, "*** FATAL ERROR: %s\n", (msg ? msg : "no message")); + fflush(stderr); + abort(); +} +#else +static void duv_fatal(duk_context *ctx, duk_errcode_t err_code, const char *msg) { + (void) ctx; (void) err_code; + fprintf(stderr, "*** FATAL ERROR: %s\n", (msg ? msg : "no message")); + fflush(stderr); + abort(); +} +#endif + int main(int argc, char *argv[]) { duk_context *ctx = NULL; uv_loop_init(&loop); @@ -494,17 +556,38 @@ int main(int argc, char *argv[]) { } // Tie loop and context together - ctx = duk_create_heap(NULL, NULL, NULL, &loop, NULL); + ctx = duk_create_heap(NULL, NULL, NULL, &loop, duv_fatal); if (!ctx) { - fprintf(stderr, "Problem initiailizing duktape heap\n"); + fprintf(stderr, "Problem initializing duktape heap\n"); return -1; } loop.data = ctx; + // Logging framework (removed in Duktape 2.x) +#if DUK_VERSION >= 19999 + duk_logging_init(ctx, 0 /*flags*/); +#endif + + // Minimal print/alert (removed in Duktape 2.x) +#if DUK_VERSION >= 19999 + duk_push_global_object(ctx); + duk_push_string(ctx, "print"); + duk_push_c_function(ctx, duv_print, DUK_VARARGS); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); + duk_push_string(ctx, "alert"); + duk_push_c_function(ctx, duv_alert, DUK_VARARGS); + duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_SET_WRITABLE | DUK_DEFPROP_SET_CONFIGURABLE); + duk_pop(ctx); +#endif + // Stash argv for later access duk_push_pointer(ctx, (void *) argv); duk_push_int(ctx, argc); +#if DUK_VERSION >= 19999 /* duk_safe_call() udata added in 2.0.0 (1.99.99 is pre-release) */ + if (duk_safe_call(ctx, duv_stash_argv, NULL, 2, 1)) { +#else if (duk_safe_call(ctx, duv_stash_argv, 2, 1)) { +#endif duv_dump_error(ctx, -1); uv_loop_close(&loop); duk_destroy_heap(ctx); diff --git a/src/schema.c b/src/schema.c index bbeb5dc..3fdb145 100644 --- a/src/schema.c +++ b/src/schema.c @@ -1,3 +1,4 @@ +#include #include "schema.h" duk_bool_t dschema_is_data(duk_context* ctx, duk_idx_t index) { @@ -16,11 +17,15 @@ void dschema_check(duk_context *ctx, const duv_schema_entry schema[]) { for (i = 0; schema[i].name; ++i) { // printf("Checking arg %d-%s\n", i, schema[i].name); if (schema[i].checker(ctx, i)) continue; - duk_dump_context_stderr(ctx); + duk_push_context_dump(ctx); + fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1)); + duk_pop(ctx); duk_error(ctx, DUK_ERR_TYPE_ERROR, "Invalid argument type for %s", schema[i].name); } if (top > i) { - duk_dump_context_stderr(ctx); + duk_push_context_dump(ctx); + fprintf(stderr, "%s\n", duk_safe_to_string(ctx, -1)); + duk_pop(ctx); duk_error(ctx, DUK_ERR_TYPE_ERROR, "Too many arguments. Expected at %d, but got %d", i, top); } } diff --git a/tcp-echo.js b/tcp-echo.js index fcf660a..efb1f54 100644 --- a/tcp-echo.js +++ b/tcp-echo.js @@ -59,7 +59,7 @@ Client.prototype.onConnect = function onConnect(err) { assert(this === client); if (err) { throw err; } this.readStart(this.onRead); - var buffer = Duktape.Buffer(3); + var buffer = (typeof ArrayBuffer.allocPlain === 'function' ? ArrayBuffer.allocPlain(3) : Duktape.Buffer(3)); buffer[0] = 0x10; buffer[1] = 0x00; buffer[2] = 0x50; diff --git a/unit-tests.js b/unit-tests.js index 3337c07..cdf3d1d 100644 --- a/unit-tests.js +++ b/unit-tests.js @@ -203,7 +203,7 @@ test("pretty printer", function () { p({ thread: new Duktape.Thread(test), buffer: timer, - dynamic: new Duktape.Buffer("Hello"), + dynamic: (typeof ArrayBuffer.allocPlain === 'function' ? Object(ArrayBuffer.allocPlain("Hello")) : new Duktape.Buffer("Hello")), pointer: new Duktape.Pointer(p), error: new Error("test"), typeError: new TypeError("test2"), @@ -216,7 +216,7 @@ test("pretty printer", function () { p({ thread: Duktape.Thread(test), - dynamic: Duktape.Buffer("Hello"), + dynamic: (typeof ArrayBuffer.allocPlain === 'function' ? ArrayBuffer.allocPlain("Hello") : Duktape.Buffer("Hello")), pointer: Duktape.Pointer(p), error: Error("test"), typeError: TypeError("test2"),