Skip to content
20 changes: 18 additions & 2 deletions duktape.cmake
Original file line number Diff line number Diff line change
@@ -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
Expand Down
56 changes: 43 additions & 13 deletions modules/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
Expand All @@ -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");
}
Expand All @@ -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.
Expand All @@ -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] + "]");
Expand Down
8 changes: 6 additions & 2 deletions repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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");
Expand Down
3 changes: 3 additions & 0 deletions src/duv.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include "uv.h"
#include "duktape.h"
#if DUK_VERSION >= 19999
#include "duk_v1_compat.h"
#endif
#include <assert.h>

#if !defined(_WIN32)
Expand Down
87 changes: 85 additions & 2 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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++) {
Expand All @@ -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)) {
Expand All @@ -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);
Expand All @@ -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);
Expand Down
9 changes: 7 additions & 2 deletions src/schema.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <stdio.h>
#include "schema.h"

duk_bool_t dschema_is_data(duk_context* ctx, duk_idx_t index) {
Expand All @@ -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);
}
}
2 changes: 1 addition & 1 deletion tcp-echo.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions unit-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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"),
Expand Down