Browse Source

Add minimal console extra

pull/767/head
Sami Vaarala 9 years ago
parent
commit
9fa7196730
  1. 4
      dist-files/Makefile.cmdline
  2. 11
      extras/console/Makefile
  3. 30
      extras/console/README.rst
  4. 157
      extras/console/duk_console.c
  5. 11
      extras/console/duk_console.h
  6. 26
      extras/console/test.c
  7. 9
      util/make_dist.py

4
dist-files/Makefile.cmdline

@ -17,6 +17,10 @@ CCLIBS = -lm
CCOPTS += -DDUK_CMDLINE_PRINTALERT_SUPPORT -I./extras/print-alert
CMDLINE_SOURCES += extras/print-alert/duk_print_alert.c
# Enable console object (console.log() etc) for command line.
CCOPTS += -DDUK_CMDLINE_CONSOLE_SUPPORT -I./extras/console
DUKTAPE_CMDLINE_SOURCES += extras/console/duk_console.c
# If you want linenoise, you can enable these. At the moment linenoise
# will cause some harmless compilation warnings.
#CCOPTS += -DDUK_CMDLINE_FANCY -I./linenoise

11
extras/console/Makefile

@ -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" ]});'

30
extras/console/README.rst

@ -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.

157
extras/console/duk_console.c

@ -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"
"})();"
);
}
}

11
extras/console/duk_console.h

@ -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 */

26
extras/console/test.c

@ -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;
}

9
util/make_dist.py

@ -156,6 +156,7 @@ def create_dist_directories(dist):
mkdir(os.path.join(dist, 'extras'))
mkdir(os.path.join(dist, 'extras', 'duk-v1-compat'))
mkdir(os.path.join(dist, 'extras', 'print-alert'))
mkdir(os.path.join(dist, 'extras', 'console'))
mkdir(os.path.join(dist, 'polyfills'))
#mkdir(os.path.join(dist, 'doc')) # Empty, so omit
mkdir(os.path.join(dist, 'licenses'))
@ -584,6 +585,14 @@ copy_files([
'Makefile'
], os.path.join('extras', 'print-alert'), os.path.join(dist, 'extras', 'print-alert'))
copy_files([
'README.rst',
'duk_console.c',
'duk_console.h',
'test.c',
'Makefile'
], os.path.join('extras', 'console'), os.path.join(dist, 'extras', 'console'))
copy_files([
'Makefile.cmdline',
'Makefile.dukdebug',

Loading…
Cancel
Save