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 <stdio.h>
10 #include <stdarg.h>
11 #include "duktape.h"
12 #include "duk_console.h"
13 #include "../../debug.h"
14
15 /* XXX: Add some form of log level filtering. */
16
17 /* XXX: Should all output be written via e.g. console.write(formattedMsg)?
18 * This would make it easier for user code to redirect all console output
19 * to a custom backend.
20 */
21
22 /* XXX: Init console object using duk_def_prop() when that call is available. */
23
duk__console_log_helper(duk_context * ctx,int level,const char * error_name)24 static duk_ret_t duk__console_log_helper(duk_context *ctx, int level, const char *error_name) {
25
26 duk_idx_t n = duk_get_top(ctx);
27 duk_idx_t i;
28
29 duk_get_global_string(ctx, "console");
30 duk_get_prop_string(ctx, -1, "format");
31
32 for (i = 0; i < n; i++) {
33 if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) {
34 /* Slow path formatting. */
35 duk_dup(ctx, -1); /* console.format */
36 duk_dup(ctx, i);
37 duk_call(ctx, 1);
38 duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */
39 }
40 }
41
42 duk_pop_2(ctx);
43
44 duk_push_string(ctx, " ");
45 duk_insert(ctx, 0);
46 duk_join(ctx, n);
47
48 if (error_name) {
49 duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1));
50 duk_push_string(ctx, "name");
51 duk_push_string(ctx, error_name);
52 duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */
53 duk_get_prop_string(ctx, -1, "stack");
54 }
55
56 JANUS_LOG(level, "%s\n", duk_to_string(ctx, -1));
57
58 return 0;
59 }
60
duk__console_assert(duk_context * ctx)61 static duk_ret_t duk__console_assert(duk_context *ctx) {
62 if (duk_to_boolean(ctx, 0)) {
63 return 0;
64 }
65 duk_remove(ctx, 0);
66
67 return duk__console_log_helper(ctx, LOG_ERR, "AssertionError");
68 }
69
duk__console_log(duk_context * ctx)70 static duk_ret_t duk__console_log(duk_context *ctx) {
71 return duk__console_log_helper(ctx, LOG_INFO, NULL);
72 }
73
duk__console_trace(duk_context * ctx)74 static duk_ret_t duk__console_trace(duk_context *ctx) {
75 return duk__console_log_helper(ctx, LOG_INFO, "Trace");
76 }
77
duk__console_info(duk_context * ctx)78 static duk_ret_t duk__console_info(duk_context *ctx) {
79 return duk__console_log_helper(ctx, LOG_INFO, NULL);
80 }
81
duk__console_warn(duk_context * ctx)82 static duk_ret_t duk__console_warn(duk_context *ctx) {
83 return duk__console_log_helper(ctx, LOG_WARN, NULL);
84 }
85
duk__console_error(duk_context * ctx)86 static duk_ret_t duk__console_error(duk_context *ctx) {
87 return duk__console_log_helper(ctx, LOG_ERR, "Error");
88 }
89
duk__console_dir(duk_context * ctx)90 static duk_ret_t duk__console_dir(duk_context *ctx) {
91 /* For now, just share the formatting of .log() */
92 return duk__console_log_helper(ctx, LOG_INFO, 0);
93 }
94
duk__console_reg_vararg_func(duk_context * ctx,duk_c_function func,const char * name,duk_uint_t flags)95 static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) {
96 duk_push_c_function(ctx, func, DUK_VARARGS);
97 duk_push_string(ctx, "name");
98 duk_push_string(ctx, name);
99 duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */
100 duk_set_magic(ctx, -1, (duk_int_t) flags);
101 duk_put_prop_string(ctx, -2, name);
102 }
103
duk_console_init(duk_context * ctx,duk_uint_t flags)104 void duk_console_init(duk_context *ctx, duk_uint_t flags) {
105 duk_uint_t flags_orig;
106
107 /* If both DUK_CONSOLE_STDOUT_ONLY and DUK_CONSOLE_STDERR_ONLY where specified,
108 * just turn off DUK_CONSOLE_STDOUT_ONLY and keep DUK_CONSOLE_STDERR_ONLY.
109 */
110 if ((flags & DUK_CONSOLE_STDOUT_ONLY) && (flags & DUK_CONSOLE_STDERR_ONLY)) {
111 flags &= ~DUK_CONSOLE_STDOUT_ONLY;
112 }
113 /* Remember the (possibly corrected) flags we received. */
114 flags_orig = flags;
115
116 duk_push_object(ctx);
117
118 /* Custom function to format objects; user can replace.
119 * For now, try JX-formatting and if that fails, fall back
120 * to ToString(v).
121 */
122 duk_eval_string(ctx,
123 "(function (E) {"
124 "return function format(v){"
125 "try{"
126 "return E('jx',v);"
127 "}catch(e){"
128 "return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */
129 "}"
130 "};"
131 "})(Duktape.enc)");
132 duk_put_prop_string(ctx, -2, "format");
133
134 flags = flags_orig;
135 if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) {
136 /* No output indicators were specified; these levels go to stdout. */
137 flags |= DUK_CONSOLE_STDOUT_ONLY;
138 }
139 duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags);
140 duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags);
141 duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */
142 duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags);
143 duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags);
144
145 flags = flags_orig;
146 if (!(flags & DUK_CONSOLE_STDOUT_ONLY) && !(flags & DUK_CONSOLE_STDERR_ONLY)) {
147 /* No output indicators were specified; these levels go to stderr. */
148 flags |= DUK_CONSOLE_STDERR_ONLY;
149 }
150 duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags);
151 duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags);
152 duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */
153 duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags);
154
155 duk_put_global_string(ctx, "console");
156
157 /* Proxy wrapping: ensures any undefined console method calls are
158 * ignored silently. This was required specifically by the
159 * DeveloperToolsWG proposal (and was implemented also by Firefox:
160 * https://bugzilla.mozilla.org/show_bug.cgi?id=629607). This is
161 * apparently no longer the preferred way of implementing console.
162 * When Proxy is enabled, whitelist at least .toJSON() to avoid
163 * confusing JX serialization of the console object.
164 */
165
166 if (flags & DUK_CONSOLE_PROXY_WRAPPER) {
167 /* Tolerate failure to initialize Proxy wrapper in case
168 * Proxy support is disabled.
169 */
170 (void) duk_peval_string_noresult(ctx,
171 "(function(){"
172 "var D=function(){};"
173 "var W={toJSON:true};" /* whitelisted */
174 "console=new Proxy(console,{"
175 "get:function(t,k){"
176 "var v=t[k];"
177 "return typeof v==='function'||W[k]?v:D;"
178 "}"
179 "});"
180 "})();"
181 );
182 }
183 }
184