PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/lib_duktape/src-separate/duk_bi_error.c

https://bitbucket.org/xixs/lua
C | 337 lines | 200 code | 58 blank | 79 comment | 45 complexity | 3721ee41dc1c8f7b1959d41cb09035aa MD5 | raw file
Possible License(s): Zlib, BSD-3-Clause, CC0-1.0, GPL-3.0, GPL-2.0, CPL-1.0, MPL-2.0-no-copyleft-exception, LGPL-2.0, LGPL-2.1, LGPL-3.0, 0BSD, Cube
  1. /*
  2. * Error built-ins
  3. */
  4. #include "duk_internal.h"
  5. DUK_INTERNAL duk_ret_t duk_bi_error_constructor_shared(duk_context *ctx) {
  6. /* Behavior for constructor and non-constructor call is
  7. * the same except for augmenting the created error. When
  8. * called as a constructor, the caller (duk_new()) will handle
  9. * augmentation; when called as normal function, we need to do
  10. * it here.
  11. */
  12. duk_hthread *thr = (duk_hthread *) ctx;
  13. duk_small_int_t bidx_prototype = duk_get_current_magic(ctx);
  14. /* same for both error and each subclass like TypeError */
  15. duk_uint_t flags_and_class = DUK_HOBJECT_FLAG_EXTENSIBLE |
  16. DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR);
  17. DUK_UNREF(thr);
  18. duk_push_object_helper(ctx, flags_and_class, bidx_prototype);
  19. /* If message is undefined, the own property 'message' is not set at
  20. * all to save property space. An empty message is inherited anyway.
  21. */
  22. if (!duk_is_undefined(ctx, 0)) {
  23. duk_to_string(ctx, 0);
  24. duk_dup(ctx, 0); /* [ message error message ] */
  25. duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
  26. }
  27. /* Augment the error if called as a normal function. __FILE__ and __LINE__
  28. * are not desirable in this case.
  29. */
  30. #ifdef DUK_USE_AUGMENT_ERROR_CREATE
  31. if (!duk_is_constructor_call(ctx)) {
  32. duk_err_augment_error_create(thr, thr, NULL, 0, 1 /*noblame_fileline*/);
  33. }
  34. #endif
  35. return 1;
  36. }
  37. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_to_string(duk_context *ctx) {
  38. /* XXX: optimize with more direct internal access */
  39. duk_push_this(ctx);
  40. (void) duk_require_hobject_or_lfunc_coerce(ctx, -1);
  41. /* [ ... this ] */
  42. duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME);
  43. if (duk_is_undefined(ctx, -1)) {
  44. duk_pop(ctx);
  45. duk_push_string(ctx, "Error");
  46. } else {
  47. duk_to_string(ctx, -1);
  48. }
  49. /* [ ... this name ] */
  50. /* XXX: Are steps 6 and 7 in E5 Section 15.11.4.4 duplicated by
  51. * accident or are they actually needed? The first ToString()
  52. * could conceivably return 'undefined'.
  53. */
  54. duk_get_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE);
  55. if (duk_is_undefined(ctx, -1)) {
  56. duk_pop(ctx);
  57. duk_push_string(ctx, "");
  58. } else {
  59. duk_to_string(ctx, -1);
  60. }
  61. /* [ ... this name message ] */
  62. if (duk_get_length(ctx, -2) == 0) {
  63. /* name is empty -> return message */
  64. return 1;
  65. }
  66. if (duk_get_length(ctx, -1) == 0) {
  67. /* message is empty -> return name */
  68. duk_pop(ctx);
  69. return 1;
  70. }
  71. duk_push_string(ctx, ": ");
  72. duk_insert(ctx, -2); /* ... name ': ' message */
  73. duk_concat(ctx, 3);
  74. return 1;
  75. }
  76. #ifdef DUK_USE_TRACEBACKS
  77. /*
  78. * Traceback handling
  79. *
  80. * The unified helper decodes the traceback and produces various requested
  81. * outputs. It should be optimized for size, and may leave garbage on stack,
  82. * only the topmost return value matters. For instance, traceback separator
  83. * and decoded strings are pushed even when looking for filename only.
  84. *
  85. * NOTE: although _Tracedata is an internal property, user code can currently
  86. * write to the array (or replace it with something other than an array).
  87. * The code below must tolerate arbitrary _Tracedata. It can throw errors
  88. * etc, but cannot cause a segfault or memory unsafe behavior.
  89. */
  90. /* constants arbitrary, chosen for small loads */
  91. #define DUK__OUTPUT_TYPE_TRACEBACK (-1)
  92. #define DUK__OUTPUT_TYPE_FILENAME 0
  93. #define DUK__OUTPUT_TYPE_LINENUMBER 1
  94. DUK_LOCAL duk_ret_t duk__traceback_getter_helper(duk_context *ctx, duk_small_int_t output_type) {
  95. duk_hthread *thr = (duk_hthread *) ctx;
  96. duk_idx_t idx_td;
  97. duk_small_int_t i; /* traceback depth fits into 16 bits */
  98. duk_small_int_t t; /* stack type fits into 16 bits */
  99. const char *str_tailcalled = " tailcalled";
  100. const char *str_strict = " strict";
  101. const char *str_construct = " construct";
  102. const char *str_prevyield = " preventsyield";
  103. const char *str_directeval = " directeval";
  104. const char *str_empty = "";
  105. DUK_ASSERT_TOP(ctx, 0); /* fixed arg count */
  106. duk_push_this(ctx);
  107. duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TRACEDATA);
  108. idx_td = duk_get_top_index(ctx);
  109. duk_push_hstring_stridx(ctx, DUK_STRIDX_NEWLINE_TAB);
  110. duk_push_this(ctx);
  111. duk_to_string(ctx, -1);
  112. /* [ ... this tracedata sep ToString(this) ] */
  113. /* XXX: skip null filename? */
  114. if (duk_check_type(ctx, idx_td, DUK_TYPE_OBJECT)) {
  115. /* Current tracedata contains 2 entries per callstack entry. */
  116. for (i = 0; ; i += 2) {
  117. duk_int_t pc;
  118. duk_int_t line;
  119. duk_int_t flags;
  120. duk_double_t d;
  121. const char *funcname;
  122. const char *filename;
  123. duk_hobject *h_func;
  124. duk_hstring *h_name;
  125. duk_require_stack(ctx, 5);
  126. duk_get_prop_index(ctx, idx_td, i);
  127. duk_get_prop_index(ctx, idx_td, i + 1);
  128. d = duk_to_number(ctx, -1);
  129. pc = (duk_int_t) DUK_FMOD(d, DUK_DOUBLE_2TO32);
  130. flags = (duk_int_t) DUK_FLOOR(d / DUK_DOUBLE_2TO32);
  131. t = (duk_small_int_t) duk_get_type(ctx, -2);
  132. if (t == DUK_TYPE_OBJECT || t == DUK_TYPE_LIGHTFUNC) {
  133. /*
  134. * Ecmascript/native function call or lightfunc call
  135. */
  136. /* [ ... v1(func) v2(pc+flags) ] */
  137. h_func = duk_get_hobject(ctx, -2); /* NULL for lightfunc */
  138. duk_get_prop_stridx(ctx, -2, DUK_STRIDX_NAME);
  139. duk_get_prop_stridx(ctx, -3, DUK_STRIDX_FILE_NAME);
  140. #if defined(DUK_USE_PC2LINE)
  141. line = duk_hobject_pc2line_query(ctx, -4, (duk_uint_fast32_t) pc);
  142. #else
  143. line = 0;
  144. #endif
  145. /* [ ... v1 v2 name filename ] */
  146. if (output_type == DUK__OUTPUT_TYPE_FILENAME) {
  147. return 1;
  148. } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) {
  149. duk_push_int(ctx, line);
  150. return 1;
  151. }
  152. h_name = duk_get_hstring(ctx, -2); /* may be NULL */
  153. funcname = (h_name == NULL || h_name == DUK_HTHREAD_STRING_EMPTY_STRING(thr)) ?
  154. "anon" : (const char *) DUK_HSTRING_GET_DATA(h_name);
  155. filename = duk_get_string(ctx, -1);
  156. filename = filename ? filename : "";
  157. DUK_ASSERT(funcname != NULL);
  158. DUK_ASSERT(filename != NULL);
  159. if (h_func == NULL) {
  160. duk_push_sprintf(ctx, "%s light%s%s%s%s%s",
  161. (const char *) funcname,
  162. (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
  163. (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty),
  164. (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
  165. (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
  166. (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
  167. } else if (DUK_HOBJECT_HAS_NATIVEFUNCTION(h_func)) {
  168. duk_push_sprintf(ctx, "%s %s native%s%s%s%s%s",
  169. (const char *) funcname,
  170. (const char *) filename,
  171. (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
  172. (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty),
  173. (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
  174. (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
  175. (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
  176. } else {
  177. duk_push_sprintf(ctx, "%s %s:%ld%s%s%s%s%s",
  178. (const char *) funcname,
  179. (const char *) filename,
  180. (long) line,
  181. (const char *) ((flags & DUK_ACT_FLAG_STRICT) ? str_strict : str_empty),
  182. (const char *) ((flags & DUK_ACT_FLAG_TAILCALLED) ? str_tailcalled : str_empty),
  183. (const char *) ((flags & DUK_ACT_FLAG_CONSTRUCT) ? str_construct : str_empty),
  184. (const char *) ((flags & DUK_ACT_FLAG_DIRECT_EVAL) ? str_directeval : str_empty),
  185. (const char *) ((flags & DUK_ACT_FLAG_PREVENT_YIELD) ? str_prevyield : str_empty));
  186. }
  187. duk_replace(ctx, -5); /* [ ... v1 v2 name filename str ] -> [ ... str v2 name filename ] */
  188. duk_pop_n(ctx, 3); /* -> [ ... str ] */
  189. } else if (t == DUK_TYPE_STRING) {
  190. /*
  191. * __FILE__ / __LINE__ entry, here 'pc' is line number directly.
  192. * Sometimes __FILE__ / __LINE__ is reported as the source for
  193. * the error (fileName, lineNumber), sometimes not.
  194. */
  195. /* [ ... v1(filename) v2(line+flags) ] */
  196. if (!(flags & DUK_TB_FLAG_NOBLAME_FILELINE)) {
  197. if (output_type == DUK__OUTPUT_TYPE_FILENAME) {
  198. duk_pop(ctx);
  199. return 1;
  200. } else if (output_type == DUK__OUTPUT_TYPE_LINENUMBER) {
  201. duk_push_int(ctx, pc);
  202. return 1;
  203. }
  204. }
  205. duk_push_sprintf(ctx, "%s:%ld",
  206. (const char *) duk_get_string(ctx, -2), (long) pc);
  207. duk_replace(ctx, -3); /* [ ... v1 v2 str ] -> [ ... str v2 ] */
  208. duk_pop(ctx); /* -> [ ... str ] */
  209. } else {
  210. /* unknown, ignore */
  211. duk_pop_2(ctx);
  212. break;
  213. }
  214. }
  215. if (i >= DUK_USE_TRACEBACK_DEPTH * 2) {
  216. /* Possibly truncated; there is no explicit truncation
  217. * marker so this is the best we can do.
  218. */
  219. duk_push_hstring_stridx(ctx, DUK_STRIDX_BRACKETED_ELLIPSIS);
  220. }
  221. }
  222. /* [ ... this tracedata sep ToString(this) str1 ... strN ] */
  223. if (output_type != DUK__OUTPUT_TYPE_TRACEBACK) {
  224. return 0;
  225. } else {
  226. duk_join(ctx, duk_get_top(ctx) - (idx_td + 2) /*count, not including sep*/);
  227. return 1;
  228. }
  229. }
  230. /* XXX: output type could be encoded into native function 'magic' value to
  231. * save space.
  232. */
  233. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) {
  234. return duk__traceback_getter_helper(ctx, DUK__OUTPUT_TYPE_TRACEBACK);
  235. }
  236. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) {
  237. return duk__traceback_getter_helper(ctx, DUK__OUTPUT_TYPE_FILENAME);
  238. }
  239. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) {
  240. return duk__traceback_getter_helper(ctx, DUK__OUTPUT_TYPE_LINENUMBER);
  241. }
  242. #undef DUK__OUTPUT_TYPE_TRACEBACK
  243. #undef DUK__OUTPUT_TYPE_FILENAME
  244. #undef DUK__OUTPUT_TYPE_LINENUMBER
  245. #else /* DUK_USE_TRACEBACKS */
  246. /*
  247. * Traceback handling when tracebacks disabled.
  248. *
  249. * The fileName / lineNumber stubs are now necessary because built-in
  250. * data will include the accessor properties in Error.prototype. If those
  251. * are removed for builds without tracebacks, these can also be removed.
  252. * 'stack' should still be present and produce a ToString() equivalent:
  253. * this is useful for user code which prints a stacktrace and expects to
  254. * see something useful. A normal stacktrace also begins with a ToString()
  255. * of the error so this makes sense.
  256. */
  257. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_stack_getter(duk_context *ctx) {
  258. /* XXX: remove this native function and map 'stack' accessor
  259. * to the toString() implementation directly.
  260. */
  261. return duk_bi_error_prototype_to_string(ctx);
  262. }
  263. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_filename_getter(duk_context *ctx) {
  264. DUK_UNREF(ctx);
  265. return 0;
  266. }
  267. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_linenumber_getter(duk_context *ctx) {
  268. DUK_UNREF(ctx);
  269. return 0;
  270. }
  271. #endif /* DUK_USE_TRACEBACKS */
  272. DUK_INTERNAL duk_ret_t duk_bi_error_prototype_nop_setter(duk_context *ctx) {
  273. /* Attempt to write 'stack', 'fileName', 'lineNumber' is a silent no-op.
  274. * User can use Object.defineProperty() to override this behavior.
  275. */
  276. DUK_ASSERT_TOP(ctx, 1); /* fixed arg count */
  277. DUK_UNREF(ctx);
  278. return 0;
  279. }