mirror of https://github.com/svaarala/duktape.git
Sami Vaarala
9 years ago
7 changed files with 248 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||
# For manual testing; say 'make' in extras/print-alert and run ./test.
|
|||
|
|||
.PHONY: test |
|||
test: |
|||
gcc -o $@ -I../../src/ -I. ../../src/duktape.c duk_console.c test.c -lm |
|||
./test 'console.assert(true, "not shown");' |
|||
./test 'console.assert(false, "shown", { foo: 123 });' |
|||
./test 'console.log(1, 2, 3, { foo: "bar" });' |
|||
./test 'a={}; b={}; a.ref=b; console.log(a,b); b.ref=a; console.log(a,b)' # circular ref |
|||
./test 'console.trace(1, 2, 3)' |
|||
./test 'console.dir({ foo: 123, bar: [ "foo", "bar" ]});' |
@ -0,0 +1,30 @@ |
|||
========================= |
|||
Minimal 'console' binding |
|||
========================= |
|||
|
|||
Duktape doesn't provide a ``console`` binding (for example ``console.log``) |
|||
by default because it would be a portability issue for some targets. Instead, |
|||
an application should provide its own ``console`` binding. This directory |
|||
contains an example binding: |
|||
|
|||
* Add ``duk_console.c`` to list of C sources to compile. |
|||
|
|||
* Ensure ``duk_console.h`` is in the include path. |
|||
|
|||
* Include the extra header in calling code:: |
|||
|
|||
#include "duktape.h" |
|||
#include "duk_console.h" |
|||
|
|||
/* After initializing the Duktape heap or when creating a new |
|||
* thread with a new global environment: |
|||
*/ |
|||
duk_console_init(ctx, 0 /*flags*/); |
|||
|
|||
Use the ``DUK_CONSOLE_PROXY_WRAPPER`` to enable a Proxy wrapper for the |
|||
console object. The wrapper allows all undefined methods (for example, |
|||
``console.foo``) to be handled as no-ops instead of throwing an error. |
|||
See ``duk_console.h`` for full flags list. |
|||
|
|||
* After these steps, ``console`` will be registered to the global object |
|||
and is ready to use. |
@ -0,0 +1,157 @@ |
|||
/*
|
|||
* Minimal 'console' binding. |
|||
* |
|||
* https://github.com/DeveloperToolsWG/console-object/blob/master/api.md
|
|||
* https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference
|
|||
* https://developer.mozilla.org/en/docs/Web/API/console
|
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
#include <stdarg.h> |
|||
#include "duktape.h" |
|||
#include "duk_console.h" |
|||
|
|||
/* XXX: Add some form of log level filtering. */ |
|||
|
|||
/* XXX: For now logs everything to stdout, V8/Node.js logs debug/info level
|
|||
* to stdout, warn and above to stderr. Should this extra do the same? |
|||
*/ |
|||
|
|||
/* XXX: Should all output be written via e.g. console.write(formattedMsg)?
|
|||
* This would make it easier for user code to redirect all console output |
|||
* to a custom backend. |
|||
*/ |
|||
|
|||
/* XXX: For now output is not flushed, add a flush flag, or maybe add flush
|
|||
* to info level and above only. |
|||
*/ |
|||
|
|||
/* XXX: Init console object using duk_def_prop() when that call is available. */ |
|||
|
|||
static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) { |
|||
duk_idx_t i, n; |
|||
|
|||
n = duk_get_top(ctx); |
|||
|
|||
duk_get_global_string(ctx, "console"); |
|||
duk_get_prop_string(ctx, -1, "format"); |
|||
|
|||
for (i = 0; i < n; i++) { |
|||
if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) { |
|||
/* Slow path formatting. */ |
|||
duk_dup(ctx, -1); /* console.format */ |
|||
duk_dup(ctx, i); |
|||
duk_call(ctx, 1); |
|||
duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */ |
|||
} |
|||
} |
|||
|
|||
duk_pop_2(ctx); |
|||
|
|||
duk_push_string(ctx, " "); |
|||
duk_insert(ctx, 0); |
|||
duk_join(ctx, n); |
|||
|
|||
if (error_name) { |
|||
duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1)); |
|||
duk_push_string(ctx, "name"); |
|||
duk_push_string(ctx, error_name); |
|||
duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */ |
|||
duk_get_prop_string(ctx, -1, "stack"); |
|||
} |
|||
|
|||
printf("%s\n", duk_to_string(ctx, -1)); |
|||
return 0; |
|||
} |
|||
|
|||
static duk_ret_t duk__console_assert(duk_context *ctx) { |
|||
if (duk_to_boolean(ctx, 0)) { |
|||
return 0; |
|||
} |
|||
duk_remove(ctx, 0); |
|||
|
|||
return duk__console_log_helper(ctx, "AssertionError"); |
|||
} |
|||
|
|||
static duk_ret_t duk__console_log(duk_context *ctx) { |
|||
return duk__console_log_helper(ctx, NULL); |
|||
} |
|||
|
|||
static duk_ret_t duk__console_trace(duk_context *ctx) { |
|||
return duk__console_log_helper(ctx, "Trace"); |
|||
} |
|||
|
|||
static duk_ret_t duk__console_info(duk_context *ctx) { |
|||
return duk__console_log_helper(ctx, NULL); |
|||
} |
|||
|
|||
static duk_ret_t duk__console_warn(duk_context *ctx) { |
|||
return duk__console_log_helper(ctx, NULL); |
|||
} |
|||
|
|||
static duk_ret_t duk__console_error(duk_context *ctx) { |
|||
return duk__console_log_helper(ctx, "Error"); |
|||
} |
|||
|
|||
static duk_ret_t duk__console_dir(duk_context *ctx) { |
|||
/* For now, just share the formatting of .log() */ |
|||
return duk__console_log_helper(ctx, 0); |
|||
} |
|||
|
|||
static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name) { |
|||
duk_push_c_function(ctx, func, DUK_VARARGS); |
|||
duk_push_string(ctx, name); |
|||
duk_put_prop_string(ctx, -2, "name"); /* Improve stacktraces by displaying function name */ |
|||
duk_put_prop_string(ctx, -2, name); |
|||
} |
|||
|
|||
void duk_console_init(duk_context *ctx, duk_uint_t flags) { |
|||
duk_push_object(ctx); |
|||
|
|||
/* Custom function to format objects; user can replace.
|
|||
* For now, try JX-formatting and if that fails, fall back |
|||
* to ToString(v). |
|||
*/ |
|||
duk_eval_string(ctx, |
|||
"(function format(v) {\n" |
|||
" try {\n" |
|||
" return Duktape.enc('jx', v);\n" |
|||
" } catch (e) {\n" |
|||
" return String(v);\n" |
|||
" }\n" |
|||
"})"); |
|||
duk_put_prop_string(ctx, -2, "format"); |
|||
|
|||
duk__console_reg_vararg_func(ctx, duk__console_assert, "assert"); |
|||
duk__console_reg_vararg_func(ctx, duk__console_log, "log"); |
|||
duk__console_reg_vararg_func(ctx, duk__console_log, "debug"); /* alias to console.log */ |
|||
duk__console_reg_vararg_func(ctx, duk__console_trace, "trace"); |
|||
duk__console_reg_vararg_func(ctx, duk__console_info, "info"); |
|||
duk__console_reg_vararg_func(ctx, duk__console_warn, "warn"); |
|||
duk__console_reg_vararg_func(ctx, duk__console_error, "error"); |
|||
duk__console_reg_vararg_func(ctx, duk__console_error, "exception"); /* alias to console.error */ |
|||
duk__console_reg_vararg_func(ctx, duk__console_dir, "dir"); |
|||
|
|||
duk_put_global_string(ctx, "console"); |
|||
|
|||
/* Proxy wrapping: ensures any undefined console method calls are
|
|||
* ignored silently. This is required specifically by the |
|||
* DeveloperToolsWG proposal (and is implemented also by Firefox: |
|||
* https://bugzilla.mozilla.org/show_bug.cgi?id=629607).
|
|||
*/ |
|||
|
|||
if (flags & DUK_CONSOLE_PROXY_WRAPPER) { |
|||
duk_eval_string_noresult(ctx, |
|||
"(function () {\n" |
|||
" var orig = console;\n" |
|||
" var dummy = function () {};\n" |
|||
" console = new Proxy(orig, {\n" |
|||
" get: function (targ, key, recv) {\n" |
|||
" var v = targ[key];\n" |
|||
" return typeof v === 'function' ? v : dummy;\n" |
|||
" }\n" |
|||
" });\n" |
|||
"})();" |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
#if !defined(DUK_CONSOLE_H_INCLUDED) |
|||
#define DUK_CONSOLE_H_INCLUDED |
|||
|
|||
#include "duktape.h" |
|||
|
|||
/* Use a proxy wrapper to make undefined methods (console.foo()) no-ops. */ |
|||
#define DUK_CONSOLE_PROXY_WRAPPER (1 << 0) |
|||
|
|||
extern void duk_console_init(duk_context *ctx, duk_uint_t flags); |
|||
|
|||
#endif /* DUK_CONSOLE_H_INCLUDED */ |
@ -0,0 +1,26 @@ |
|||
#include <stdio.h> |
|||
#include "duktape.h" |
|||
#include "duk_console.h" |
|||
|
|||
int main(int argc, char *argv[]) { |
|||
duk_context *ctx; |
|||
int i; |
|||
|
|||
ctx = duk_create_heap_default(); |
|||
if (!ctx) { |
|||
return 1; |
|||
} |
|||
|
|||
duk_console_init(ctx, DUK_CONSOLE_PROXY_WRAPPER /*flags*/); |
|||
|
|||
for (i = 1; i < argc; i++) { |
|||
printf("Evaling: %s\n", argv[i]); |
|||
(void) duk_peval_string(ctx, argv[i]); |
|||
printf("--> %s\n", duk_safe_to_string(ctx, -1)); |
|||
duk_pop(ctx); |
|||
} |
|||
|
|||
printf("Done\n"); |
|||
duk_destroy_heap(ctx); |
|||
return 0; |
|||
} |
Loading…
Reference in new issue