File Coverage

duk_console.c
Criterion Covered Total %
statement 86 88 97.7
branch 15 24 62.5
condition n/a
subroutine n/a
pod n/a
total 101 112 90.1


line stmt bran cond sub pod time code
1             /*
2             * Minimal 'console' binding.
3             *
4             * https://github.com/DeveloperToolsWG/console-object/blob/master/api.md
5             * https://developers.google.com/web/tools/chrome-devtools/debug/console/console-reference
6             * https://developer.mozilla.org/en/docs/Web/API/console
7             */
8              
9             #include
10             #include "duktape.h"
11             #include "duk_console.h"
12              
13             /* XXX: Add some form of log level filtering. */
14              
15             /* XXX: Should all output be written via e.g. console.write(formattedMsg)?
16             * This would make it easier for user code to redirect all console output
17             * to a custom backend.
18             */
19              
20             /* XXX: Init console object using duk_def_prop() when that call is available. */
21              
22             typedef struct ConsoleConfig {
23             ConsoleHandler* handler;
24             void* data;
25             } ConsoleConfig;
26              
27             static ConsoleConfig console_config;
28              
29 35           int duk_console_log(duk_uint_t flags, const char* fmt, ...)
30             {
31 35           int ret = 0;
32 35 50         if (console_config.handler) {
33             va_list ap;
34 35           va_start(ap, fmt);
35 35           ret = console_config.handler(flags, console_config.data, fmt, ap);
36 35           va_end(ap);
37             }
38 35           return ret;
39             }
40              
41 263           void duk_console_register_handler(ConsoleHandler* handler, void* data)
42             {
43 263           console_config.handler = handler;
44 263           console_config.data = data;
45 263           }
46              
47 23           static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) {
48 23           duk_uint_t flags = (duk_uint_t) duk_get_current_magic(ctx);
49 23           duk_idx_t n = duk_get_top(ctx);
50             duk_idx_t i;
51              
52 23           duk_get_global_string(ctx, "console");
53 23           duk_get_prop_string(ctx, -1, "format");
54              
55 70 100         for (i = 0; i < n; i++) {
56 47 100         if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) {
57             /* Slow path formatting. */
58 16           duk_dup(ctx, -1); /* console.format */
59 16           duk_dup(ctx, i);
60 16           duk_call(ctx, 1);
61 16           duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */
62             }
63             }
64              
65 23           duk_pop_2(ctx);
66              
67 23           duk_push_string(ctx, " ");
68 23           duk_insert(ctx, 0);
69 23           duk_join(ctx, n);
70              
71 23 100         if (error_name) {
72 7           duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1));
73 7           duk_push_string(ctx, "name");
74 7           duk_push_string(ctx, error_name);
75 7           duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */
76 7           duk_get_prop_string(ctx, -1, "stack");
77             }
78              
79 23           duk_console_log(flags, "%s\n", duk_to_string(ctx, -1));
80 23           return 0;
81             }
82              
83 2           static duk_ret_t duk__console_assert(duk_context *ctx) {
84 2 100         if (duk_to_boolean(ctx, 0)) {
85 1           return 0;
86             }
87 1           duk_remove(ctx, 0);
88              
89 1           return duk__console_log_helper(ctx, "AssertionError");
90             }
91              
92 10           static duk_ret_t duk__console_log(duk_context *ctx) {
93 10           return duk__console_log_helper(ctx, NULL);
94             }
95              
96 2           static duk_ret_t duk__console_trace(duk_context *ctx) {
97 2           return duk__console_log_helper(ctx, "Trace");
98             }
99              
100 2           static duk_ret_t duk__console_info(duk_context *ctx) {
101 2           return duk__console_log_helper(ctx, NULL);
102             }
103              
104 2           static duk_ret_t duk__console_warn(duk_context *ctx) {
105 2           return duk__console_log_helper(ctx, NULL);
106             }
107              
108 4           static duk_ret_t duk__console_error(duk_context *ctx) {
109 4           return duk__console_log_helper(ctx, "Error");
110             }
111              
112 2           static duk_ret_t duk__console_dir(duk_context *ctx) {
113             /* For now, just share the formatting of .log() */
114 2           return duk__console_log_helper(ctx, 0);
115             }
116              
117 2367           static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) {
118 2367           duk_push_c_function(ctx, func, DUK_VARARGS);
119 2367           duk_push_string(ctx, "name");
120 2367           duk_push_string(ctx, name);
121 2367           duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */
122 2367           duk_set_magic(ctx, -1, (duk_int_t) flags);
123 2367           duk_put_prop_string(ctx, -2, name);
124 2367           }
125              
126 263           void duk_console_init(duk_context *ctx, duk_uint_t flags) {
127             duk_uint_t flags_orig;
128              
129             /* If both DUK_CONSOLE_TO_STDOUT and DUK_CONSOLE_TO_STDERR where specified,
130             * just turn off DUK_CONSOLE_TO_STDOUT and keep DUK_CONSOLE_TO_STDERR. */
131 263 50         if ((flags & DUK_CONSOLE_TO_STDOUT) &&
    0          
132 0           (flags & DUK_CONSOLE_TO_STDERR)) {
133 0           flags &= ~DUK_CONSOLE_TO_STDOUT;
134             }
135             /* Remember the (possibly corrected) flags we received. */
136 263           flags_orig = flags;
137              
138 263           duk_push_object(ctx);
139              
140             /* Custom function to format objects; user can replace.
141             * For now, try JX-formatting and if that fails, fall back
142             * to ToString(v).
143             */
144 263           duk_eval_string(ctx,
145             "(function (E) {"
146             "return function format(v){"
147             "try{"
148             "return E('jx',v);"
149             "}catch(e){"
150             "return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */
151             "}"
152             "};"
153             "})(Duktape.enc)");
154 263           duk_put_prop_string(ctx, -2, "format");
155              
156 263           flags = flags_orig;
157 263 50         if (!(flags & DUK_CONSOLE_TO_STDOUT) &&
    50          
158 263           !(flags & DUK_CONSOLE_TO_STDERR)) {
159             /* No output indicators were specified; these levels go to stdout. */
160 263           flags |= DUK_CONSOLE_TO_STDOUT;
161             }
162 263           duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags);
163 263           duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags);
164 263           duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */
165 263           duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags);
166 263           duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags);
167              
168 263           flags = flags_orig;
169 263 50         if (!(flags & DUK_CONSOLE_TO_STDOUT) &&
    50          
170 263           !(flags & DUK_CONSOLE_TO_STDERR)) {
171             /* No output indicators were specified; these levels go to stderr. */
172 263           flags |= DUK_CONSOLE_TO_STDERR;
173             }
174 263           duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags);
175 263           duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags);
176 263           duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */
177 263           duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags);
178              
179 263           duk_put_global_string(ctx, "console");
180              
181             /* Proxy wrapping: ensures any undefined console method calls are
182             * ignored silently. This is required specifically by the
183             * DeveloperToolsWG proposal (and is implemented also by Firefox:
184             * https://bugzilla.mozilla.org/show_bug.cgi?id=629607).
185             */
186              
187 263 50         if (flags & DUK_CONSOLE_PROXY_WRAPPER) {
188             /* Tolerate errors: Proxy may be disabled. */
189 263           duk_peval_string_noresult(ctx,
190             "(function(){"
191             "var D=function(){};"
192             "console=new Proxy(console,{"
193             "get:function(t,k){"
194             "var v=t[k];"
195             "return typeof v==='function'?v:D;"
196             "}"
197             "});"
198             "})();"
199             );
200             }
201 263           }