duk_console.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  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. #include <stdio.h>
  9. #include <stdarg.h>
  10. #include "duktape.h"
  11. #include "duk_console.h"
  12. /* XXX: Add some form of log level filtering. */
  13. /* XXX: For now logs everything to stdout, V8/Node.js logs debug/info level
  14. * to stdout, warn and above to stderr. Should this extra do the same?
  15. */
  16. /* XXX: Should all output be written via e.g. console.write(formattedMsg)?
  17. * This would make it easier for user code to redirect all console output
  18. * to a custom backend.
  19. */
  20. /* XXX: Init console object using duk_def_prop() when that call is available. */
  21. static duk_ret_t duk__console_log_helper(duk_context *ctx, const char *error_name) {
  22. duk_idx_t i, n;
  23. duk_uint_t flags;
  24. flags = (duk_uint_t) duk_get_current_magic(ctx);
  25. n = duk_get_top(ctx);
  26. duk_get_global_string(ctx, "console");
  27. duk_get_prop_string(ctx, -1, "format");
  28. for (i = 0; i < n; i++) {
  29. if (duk_check_type_mask(ctx, i, DUK_TYPE_MASK_OBJECT)) {
  30. /* Slow path formatting. */
  31. duk_dup(ctx, -1); /* console.format */
  32. duk_dup(ctx, i);
  33. duk_call(ctx, 1);
  34. duk_replace(ctx, i); /* arg[i] = console.format(arg[i]); */
  35. }
  36. }
  37. duk_pop_2(ctx);
  38. duk_push_string(ctx, " ");
  39. duk_insert(ctx, 0);
  40. duk_join(ctx, n);
  41. if (error_name) {
  42. duk_push_error_object(ctx, DUK_ERR_ERROR, "%s", duk_require_string(ctx, -1));
  43. duk_push_string(ctx, "name");
  44. duk_push_string(ctx, error_name);
  45. duk_def_prop(ctx, -3, DUK_DEFPROP_FORCE | DUK_DEFPROP_HAVE_VALUE); /* to get e.g. 'Trace: 1 2 3' */
  46. duk_get_prop_string(ctx, -1, "stack");
  47. }
  48. fprintf(stdout, "%s\n", duk_to_string(ctx, -1));
  49. if (flags & DUK_CONSOLE_FLUSH) {
  50. fflush(stdout);
  51. }
  52. return 0;
  53. }
  54. static duk_ret_t duk__console_assert(duk_context *ctx) {
  55. if (duk_to_boolean(ctx, 0)) {
  56. return 0;
  57. }
  58. duk_remove(ctx, 0);
  59. return duk__console_log_helper(ctx, "AssertionError");
  60. }
  61. static duk_ret_t duk__console_log(duk_context *ctx) {
  62. return duk__console_log_helper(ctx, NULL);
  63. }
  64. static duk_ret_t duk__console_trace(duk_context *ctx) {
  65. return duk__console_log_helper(ctx, "Trace");
  66. }
  67. static duk_ret_t duk__console_info(duk_context *ctx) {
  68. return duk__console_log_helper(ctx, NULL);
  69. }
  70. static duk_ret_t duk__console_warn(duk_context *ctx) {
  71. return duk__console_log_helper(ctx, NULL);
  72. }
  73. static duk_ret_t duk__console_error(duk_context *ctx) {
  74. return duk__console_log_helper(ctx, "Error");
  75. }
  76. static duk_ret_t duk__console_dir(duk_context *ctx) {
  77. /* For now, just share the formatting of .log() */
  78. return duk__console_log_helper(ctx, 0);
  79. }
  80. static void duk__console_reg_vararg_func(duk_context *ctx, duk_c_function func, const char *name, duk_uint_t flags) {
  81. duk_push_c_function(ctx, func, DUK_VARARGS);
  82. duk_push_string(ctx, "name");
  83. duk_push_string(ctx, name);
  84. duk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_FORCE); /* Improve stacktraces by displaying function name */
  85. duk_set_magic(ctx, -1, (duk_int_t) flags);
  86. duk_put_prop_string(ctx, -2, name);
  87. }
  88. void duk_console_init(duk_context *ctx, duk_uint_t flags) {
  89. duk_push_object(ctx);
  90. /* Custom function to format objects; user can replace.
  91. * For now, try JX-formatting and if that fails, fall back
  92. * to ToString(v).
  93. */
  94. duk_eval_string(ctx,
  95. "(function (E) {"
  96. "return function format(v){"
  97. "try{"
  98. "return E('jx',v);"
  99. "}catch(e){"
  100. "return String(v);" /* String() allows symbols, ToString() internal algorithm doesn't. */
  101. "}"
  102. "};"
  103. "})(Duktape.enc)");
  104. duk_put_prop_string(ctx, -2, "format");
  105. duk__console_reg_vararg_func(ctx, duk__console_assert, "assert", flags);
  106. duk__console_reg_vararg_func(ctx, duk__console_log, "log", flags);
  107. duk__console_reg_vararg_func(ctx, duk__console_log, "debug", flags); /* alias to console.log */
  108. duk__console_reg_vararg_func(ctx, duk__console_trace, "trace", flags);
  109. duk__console_reg_vararg_func(ctx, duk__console_info, "info", flags);
  110. duk__console_reg_vararg_func(ctx, duk__console_warn, "warn", flags);
  111. duk__console_reg_vararg_func(ctx, duk__console_error, "error", flags);
  112. duk__console_reg_vararg_func(ctx, duk__console_error, "exception", flags); /* alias to console.error */
  113. duk__console_reg_vararg_func(ctx, duk__console_dir, "dir", flags);
  114. duk_put_global_string(ctx, "console");
  115. /* Proxy wrapping: ensures any undefined console method calls are
  116. * ignored silently. This is required specifically by the
  117. * DeveloperToolsWG proposal (and is implemented also by Firefox:
  118. * https://bugzilla.mozilla.org/show_bug.cgi?id=629607).
  119. */
  120. if (flags & DUK_CONSOLE_PROXY_WRAPPER) {
  121. /* Tolerate errors: Proxy may be disabled. */
  122. duk_peval_string_noresult(ctx,
  123. "(function(){"
  124. "var D=function(){};"
  125. "console=new Proxy(console,{"
  126. "get:function(t,k){"
  127. "var v=t[k];"
  128. "return typeof v==='function'?v:D;"
  129. "}"
  130. "});"
  131. "})();"
  132. );
  133. }
  134. }