1 /*
2  *  Create and throw an Ecmascript error object based on a code and a message.
3  *
4  *  Used when we throw errors internally.  Ecmascript generated error objects
5  *  are created by Ecmascript code, and the throwing is handled by the bytecode
6  *  executor.
7  */
8 
9 #include "duk_internal.h"
10 
11 /*
12  *  Create and throw an error (originating from Duktape internally)
13  *
14  *  Push an error object on top of the stack, possibly throw augmenting
15  *  the error, and finally longjmp.
16  *
17  *  If an error occurs while we're dealing with the current error, we might
18  *  enter an infinite recursion loop.  This is prevented by detecting a
19  *  "double fault" through the heap->handling_error flag; the recursion
20  *  then stops at the second level.
21  */
22 
23 #ifdef DUK_USE_VERBOSE_ERRORS
duk_err_create_and_throw(duk_hthread * thr,duk_errcode_t code,const char * msg,const char * filename,duk_int_t line)24 DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code, const char *msg, const char *filename, duk_int_t line) {
25 #else
26 DUK_INTERNAL void duk_err_create_and_throw(duk_hthread *thr, duk_errcode_t code) {
27 #endif
28 	duk_context *ctx = (duk_context *) thr;
29 	duk_bool_t double_error = thr->heap->handling_error;
30 
31 #ifdef DUK_USE_VERBOSE_ERRORS
32 	DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld, msg=%s, filename=%s, line=%ld",
33 	                   (long) code, (const char *) msg,
34 	                   (const char *) filename, (long) line));
35 #else
36 	DUK_DD(DUK_DDPRINT("duk_err_create_and_throw(): code=%ld", (long) code));
37 #endif
38 
39 	DUK_ASSERT(thr != NULL);
40 	DUK_ASSERT(ctx != NULL);
41 
42 	thr->heap->handling_error = 1;
43 
44 	if (!double_error) {
45 		/* Allow headroom for calls during error handling (see GH-191).
46 		 * We allow space for 10 additional recursions, with one extra
47 		 * for, e.g. a print() call at the deepest level.
48 		 */
49 		DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX);
50 		thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11;
51 	}
52 
53 	DUK_ASSERT(thr->callstack_max == DUK_CALLSTACK_DEFAULT_MAX + DUK_CALLSTACK_GROW_STEP + 11);  /* just making sure */
54 
55 	/* Sync so that augmentation sees up-to-date activations, NULL
56 	 * thr->ptr_curr_pc so that it's not used if side effects occur
57 	 * in augmentation or longjmp handling.
58 	 */
59 	duk_hthread_sync_and_null_currpc(thr);
60 
61 	/*
62 	 *  Create and push an error object onto the top of stack.
63 	 *  If a "double error" occurs, use a fixed error instance
64 	 *  to avoid further trouble.
65 	 */
66 
67 	/* XXX: if attempt to push beyond allocated valstack, this double fault
68 	 * handling fails miserably.  We should really write the double error
69 	 * directly to thr->heap->lj.value1 and avoid valstack use entirely.
70 	 */
71 
72 	if (double_error) {
73 		if (thr->builtins[DUK_BIDX_DOUBLE_ERROR]) {
74 			DUK_D(DUK_DPRINT("double fault detected -> push built-in fixed 'double error' instance"));
75 			duk_push_hobject_bidx(ctx, DUK_BIDX_DOUBLE_ERROR);
76 		} else {
77 			DUK_D(DUK_DPRINT("double fault detected; there is no built-in fixed 'double error' instance "
78 			                 "-> push the error code as a number"));
79 			duk_push_int(ctx, (duk_int_t) code);
80 		}
81 	} else {
82 		/* Error object is augmented at its creation here. */
83 		duk_require_stack(ctx, 1);
84 		/* XXX: unnecessary '%s' formatting here, but cannot use
85 		 * 'msg' as a format string directly.
86 		 */
87 #ifdef DUK_USE_VERBOSE_ERRORS
88 		duk_push_error_object_raw(ctx,
89 		                          code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
90 		                          filename,
91 		                          line,
92 		                          "%s",
93 		                          (const char *) msg);
94 #else
95 		duk_push_error_object_raw(ctx,
96 		                          code | DUK_ERRCODE_FLAG_NOBLAME_FILELINE,
97 		                          NULL,
98 		                          0,
99 		                          NULL);
100 #endif
101 	}
102 
103 	/*
104 	 *  Augment error (throw time), unless alloc/double error
105 	 */
106 
107 	if (double_error || code == DUK_ERR_ALLOC_ERROR) {
108 		DUK_D(DUK_DPRINT("alloc or double error: skip throw augmenting to avoid further trouble"));
109 	} else {
110 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
111 		DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT (before throw augment)",
112 		                     (duk_tval *) duk_get_tval(ctx, -1)));
113 		duk_err_augment_error_throw(thr);
114 #endif
115 	}
116 
117 	/*
118 	 *  Finally, longjmp
119 	 */
120 
121 	duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW);
122 
123 	thr->callstack_max = DUK_CALLSTACK_DEFAULT_MAX;  /* reset callstack limit */
124 	thr->heap->handling_error = 0;
125 
126 	DUK_DDD(DUK_DDDPRINT("THROW ERROR (INTERNAL): %!iT, %!iT (after throw augment)",
127 	                     (duk_tval *) &thr->heap->lj.value1, (duk_tval *) &thr->heap->lj.value2));
128 
129 	duk_err_longjmp(thr);
130 	DUK_UNREACHABLE();
131 }
132 
133 /*
134  *  Helper for C function call negative return values.
135  */
136 
137 DUK_INTERNAL void duk_error_throw_from_negative_rc(duk_hthread *thr, duk_ret_t rc) {
138 	duk_context *ctx = (duk_context *) thr;
139 	const char *msg;
140 	duk_errcode_t code;
141 
142 	DUK_ASSERT(thr != NULL);
143 	DUK_ASSERT(rc < 0);
144 
145 	/* XXX: this generates quite large code - perhaps select the error
146 	 * class based on the code and then just use the error 'name'?
147 	 */
148 	/* XXX: shared strings */
149 
150 	code = -rc;
151 
152 	switch (rc) {
153 	case DUK_RET_UNIMPLEMENTED_ERROR:  msg = "unimplemented"; break;
154 	case DUK_RET_UNSUPPORTED_ERROR:    msg = "unsupported"; break;
155 	case DUK_RET_INTERNAL_ERROR:       msg = "internal"; break;
156 	case DUK_RET_ALLOC_ERROR:          msg = "alloc"; break;
157 	case DUK_RET_ASSERTION_ERROR:      msg = "assertion"; break;
158 	case DUK_RET_API_ERROR:            msg = "api"; break;
159 	case DUK_RET_UNCAUGHT_ERROR:       msg = "uncaught"; break;
160 	case DUK_RET_ERROR:                msg = "error"; break;
161 	case DUK_RET_EVAL_ERROR:           msg = "eval"; break;
162 	case DUK_RET_RANGE_ERROR:          msg = "range"; break;
163 	case DUK_RET_REFERENCE_ERROR:      msg = "reference"; break;
164 	case DUK_RET_SYNTAX_ERROR:         msg = "syntax"; break;
165 	case DUK_RET_TYPE_ERROR:           msg = "type"; break;
166 	case DUK_RET_URI_ERROR:            msg = "uri"; break;
167 	default:                           msg = "unknown"; break;
168 	}
169 
170 	DUK_ASSERT(msg != NULL);
171 
172 	/*
173 	 *  The __FILE__ and __LINE__ information is intentionally not used in the
174 	 *  creation of the error object, as it isn't useful in the tracedata.  The
175 	 *  tracedata still contains the function which returned the negative return
176 	 *  code, and having the file/line of this function isn't very useful.
177 	 */
178 
179 	duk_error_raw(ctx, code, NULL, 0, "%s error (rc %ld)", (const char *) msg, (long) rc);
180 	DUK_UNREACHABLE();
181 }
182