1 /*
2  *  Bytecode dump/load
3  *
4  *  The bytecode load primitive is more important performance-wise than the
5  *  dump primitive.
6  *
7  *  Unlike most Duktape API calls, bytecode dump/load is not guaranteed to be
8  *  memory safe for invalid arguments - caller beware!  There's little point
9  *  in trying to achieve memory safety unless bytecode instructions are also
10  *  validated which is not easy to do with indirect register references etc.
11  */
12 
13 #include "duk_internal.h"
14 
15 #if defined(DUK_USE_BYTECODE_DUMP_SUPPORT)
16 
17 #define DUK__SER_MARKER  0xbf
18 #define DUK__SER_STRING  0x00
19 #define DUK__SER_NUMBER  0x01
20 #define DUK__BYTECODE_INITIAL_ALLOC 256
21 #define DUK__NO_FORMALS  0xffffffffUL
22 
23 /*
24  *  Dump/load helpers, xxx_raw() helpers do no buffer checks
25  */
26 
duk__load_string_raw(duk_hthread * thr,const duk_uint8_t * p)27 DUK_LOCAL const duk_uint8_t *duk__load_string_raw(duk_hthread *thr, const duk_uint8_t *p) {
28 	duk_uint32_t len;
29 
30 	len = DUK_RAW_READINC_U32_BE(p);
31 	duk_push_lstring(thr, (const char *) p, len);
32 	p += len;
33 	return p;
34 }
35 
duk__load_buffer_raw(duk_hthread * thr,const duk_uint8_t * p)36 DUK_LOCAL const duk_uint8_t *duk__load_buffer_raw(duk_hthread *thr, const duk_uint8_t *p) {
37 	duk_uint32_t len;
38 	duk_uint8_t *buf;
39 
40 	len = DUK_RAW_READINC_U32_BE(p);
41 	buf = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, (duk_size_t) len);
42 	DUK_ASSERT(buf != NULL);
43 	duk_memcpy((void *) buf, (const void *) p, (size_t) len);
44 	p += len;
45 	return p;
46 }
47 
duk__dump_hstring_raw(duk_uint8_t * p,duk_hstring * h)48 DUK_LOCAL duk_uint8_t *duk__dump_hstring_raw(duk_uint8_t *p, duk_hstring *h) {
49 	duk_size_t len;
50 	duk_uint32_t tmp32;
51 
52 	DUK_ASSERT(h != NULL);
53 
54 	len = DUK_HSTRING_GET_BYTELEN(h);
55 	DUK_ASSERT(len <= 0xffffffffUL);  /* string limits */
56 	tmp32 = (duk_uint32_t) len;
57 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
58 	duk_memcpy((void *) p,
59 	           (const void *) DUK_HSTRING_GET_DATA(h),
60 	           len);
61 	p += len;
62 	return p;
63 }
64 
duk__dump_hbuffer_raw(duk_hthread * thr,duk_uint8_t * p,duk_hbuffer * h)65 DUK_LOCAL duk_uint8_t *duk__dump_hbuffer_raw(duk_hthread *thr, duk_uint8_t *p, duk_hbuffer *h) {
66 	duk_size_t len;
67 	duk_uint32_t tmp32;
68 
69 	DUK_ASSERT(thr != NULL);
70 	DUK_ASSERT(h != NULL);
71 	DUK_UNREF(thr);
72 
73 	len = DUK_HBUFFER_GET_SIZE(h);
74 	DUK_ASSERT(len <= 0xffffffffUL);  /* buffer limits */
75 	tmp32 = (duk_uint32_t) len;
76 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
77 	/* When len == 0, buffer data pointer may be NULL. */
78 	duk_memcpy_unsafe((void *) p,
79 	                  (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h),
80 	                  len);
81 	p += len;
82 	return p;
83 }
84 
duk__dump_string_prop(duk_hthread * thr,duk_uint8_t * p,duk_bufwriter_ctx * bw_ctx,duk_hobject * func,duk_small_uint_t stridx)85 DUK_LOCAL duk_uint8_t *duk__dump_string_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx) {
86 	duk_hstring *h_str;
87 	duk_tval *tv;
88 
89 	tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx);
90 	if (tv != NULL && DUK_TVAL_IS_STRING(tv)) {
91 		h_str = DUK_TVAL_GET_STRING(tv);
92 		DUK_ASSERT(h_str != NULL);
93 	} else {
94 		h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr);
95 		DUK_ASSERT(h_str != NULL);
96 	}
97 	DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
98 	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(h_str), p);
99 	p = duk__dump_hstring_raw(p, h_str);
100 	return p;
101 }
102 
duk__dump_buffer_prop(duk_hthread * thr,duk_uint8_t * p,duk_bufwriter_ctx * bw_ctx,duk_hobject * func,duk_small_uint_t stridx)103 DUK_LOCAL duk_uint8_t *duk__dump_buffer_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx) {
104 	duk_tval *tv;
105 
106 	tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx);
107 	if (tv != NULL && DUK_TVAL_IS_BUFFER(tv)) {
108 		duk_hbuffer *h_buf;
109 		h_buf = DUK_TVAL_GET_BUFFER(tv);
110 		DUK_ASSERT(h_buf != NULL);
111 		DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
112 		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HBUFFER_GET_SIZE(h_buf), p);
113 		p = duk__dump_hbuffer_raw(thr, p, h_buf);
114 	} else {
115 		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
116 		DUK_RAW_WRITEINC_U32_BE(p, 0);
117 	}
118 	return p;
119 }
120 
duk__dump_uint32_prop(duk_hthread * thr,duk_uint8_t * p,duk_bufwriter_ctx * bw_ctx,duk_hobject * func,duk_small_uint_t stridx,duk_uint32_t def_value)121 DUK_LOCAL duk_uint8_t *duk__dump_uint32_prop(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func, duk_small_uint_t stridx, duk_uint32_t def_value) {
122 	duk_tval *tv;
123 	duk_uint32_t val;
124 
125 	tv = duk_hobject_find_entry_tval_ptr_stridx(thr->heap, (duk_hobject *) func, stridx);
126 	if (tv != NULL && DUK_TVAL_IS_NUMBER(tv)) {
127 		val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv);
128 	} else {
129 		val = def_value;
130 	}
131 	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
132 	DUK_RAW_WRITEINC_U32_BE(p, val);
133 	return p;
134 }
135 
duk__dump_varmap(duk_hthread * thr,duk_uint8_t * p,duk_bufwriter_ctx * bw_ctx,duk_hobject * func)136 DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) {
137 	duk_hobject *h;
138 
139 	h = duk_hobject_get_varmap(thr, (duk_hobject *) func);
140 	if (h != NULL) {
141 		duk_uint_fast32_t i;
142 
143 		/* We know _Varmap only has own properties so walk property
144 		 * table directly.  We also know _Varmap is dense and all
145 		 * values are numbers; assert for these.  GC and finalizers
146 		 * shouldn't affect _Varmap so side effects should be fine.
147 		 */
148 		for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
149 			duk_hstring *key;
150 			duk_tval *tv_val;
151 			duk_uint32_t val;
152 
153 			key = DUK_HOBJECT_E_GET_KEY(thr->heap, h, i);
154 			DUK_ASSERT(key != NULL);  /* _Varmap is dense */
155 			DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i));
156 			tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i);
157 			DUK_ASSERT(tv_val != NULL);
158 			DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_val));  /* known to be number; in fact an integer */
159 #if defined(DUK_USE_FASTINT)
160 			DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv_val));
161 			DUK_ASSERT(DUK_TVAL_GET_FASTINT(tv_val) == (duk_int64_t) DUK_TVAL_GET_FASTINT_U32(tv_val));  /* known to be 32-bit */
162 			val = DUK_TVAL_GET_FASTINT_U32(tv_val);
163 #else
164 			val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv_val);
165 #endif
166 
167 			DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
168 			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(key) + 4U, p);
169 			p = duk__dump_hstring_raw(p, key);
170 			DUK_RAW_WRITEINC_U32_BE(p, val);
171 		}
172 	}
173 	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
174 	DUK_RAW_WRITEINC_U32_BE(p, 0);  /* end of _Varmap */
175 	return p;
176 }
177 
duk__dump_formals(duk_hthread * thr,duk_uint8_t * p,duk_bufwriter_ctx * bw_ctx,duk_hobject * func)178 DUK_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) {
179 	duk_harray *h;
180 
181 	h = duk_hobject_get_formals(thr, (duk_hobject *) func);
182 	if (h != NULL) {
183 		duk_uint32_t i;
184 
185 		/* Here we rely on _Formals being a dense array containing
186 		 * strings.  This should be the case unless _Formals has been
187 		 * tweaked by the application (which we don't support right
188 		 * now).
189 		 */
190 
191 		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
192 		DUK_ASSERT(h->length != DUK__NO_FORMALS);  /* limits */
193 		DUK_RAW_WRITEINC_U32_BE(p, h->length);
194 
195 		for (i = 0; i < h->length; i++) {
196 			duk_tval *tv_val;
197 			duk_hstring *varname;
198 
199 			tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, (duk_hobject *) h, i);
200 			DUK_ASSERT(tv_val != NULL);
201 			DUK_ASSERT(DUK_TVAL_IS_STRING(tv_val));
202 
203 			varname = DUK_TVAL_GET_STRING(tv_val);
204 			DUK_ASSERT(varname != NULL);
205 			DUK_ASSERT(DUK_HSTRING_GET_BYTELEN(varname) >= 1);
206 
207 			DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
208 			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U + DUK_HSTRING_GET_BYTELEN(varname), p);
209 			p = duk__dump_hstring_raw(p, varname);
210 		}
211 	} else {
212 		DUK_DD(DUK_DDPRINT("dumping function without _Formals, emit marker to indicate missing _Formals"));
213 		p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4U, p);
214 		DUK_RAW_WRITEINC_U32_BE(p, DUK__NO_FORMALS);  /* marker: no formals */
215 	}
216 	return p;
217 }
218 
duk__dump_func(duk_hthread * thr,duk_hcompfunc * func,duk_bufwriter_ctx * bw_ctx,duk_uint8_t * p)219 static duk_uint8_t *duk__dump_func(duk_hthread *thr, duk_hcompfunc *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) {
220 	duk_tval *tv, *tv_end;
221 	duk_instr_t *ins, *ins_end;
222 	duk_hobject **fn, **fn_end;
223 	duk_hstring *h_str;
224 	duk_uint32_t count_instr;
225 	duk_uint32_t tmp32;
226 	duk_uint16_t tmp16;
227 	duk_double_t d;
228 
229 	DUK_DD(DUK_DDPRINT("dumping function %p to %p: "
230 	                   "consts=[%p,%p[ (%ld bytes, %ld items), "
231 	                   "funcs=[%p,%p[ (%ld bytes, %ld items), "
232 	                   "code=[%p,%p[ (%ld bytes, %ld items)",
233 	                   (void *) func,
234 	                   (void *) p,
235 	                   (void *) DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func),
236 	                   (void *) DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func),
237 	                   (long) DUK_HCOMPFUNC_GET_CONSTS_SIZE(thr->heap, func),
238 	                   (long) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func),
239 	                   (void *) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func),
240 	                   (void *) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func),
241 	                   (long) DUK_HCOMPFUNC_GET_FUNCS_SIZE(thr->heap, func),
242 	                   (long) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func),
243 	                   (void *) DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func),
244 	                   (void *) DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func),
245 	                   (long) DUK_HCOMPFUNC_GET_CODE_SIZE(thr->heap, func),
246 	                   (long) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func)));
247 
248 	DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL);  /* ensures no overflow */
249 	count_instr = (duk_uint32_t) DUK_HCOMPFUNC_GET_CODE_COUNT(thr->heap, func);
250 	p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3U * 4U + 2U * 2U + 3U * 4U + count_instr * 4U, p);
251 
252 	/* Fixed header info. */
253 	tmp32 = count_instr;
254 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
255 	tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_CONSTS_COUNT(thr->heap, func);
256 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
257 	tmp32 = (duk_uint32_t) DUK_HCOMPFUNC_GET_FUNCS_COUNT(thr->heap, func);
258 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
259 	tmp16 = func->nregs;
260 	DUK_RAW_WRITEINC_U16_BE(p, tmp16);
261 	tmp16 = func->nargs;
262 	DUK_RAW_WRITEINC_U16_BE(p, tmp16);
263 #if defined(DUK_USE_DEBUGGER_SUPPORT)
264 	tmp32 = func->start_line;
265 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
266 	tmp32 = func->end_line;
267 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
268 #else
269 	DUK_RAW_WRITEINC_U32_BE(p, 0);
270 	DUK_RAW_WRITEINC_U32_BE(p, 0);
271 #endif
272 	tmp32 = DUK_HEAPHDR_GET_FLAGS((duk_heaphdr *) func);  /* masks flags, only duk_hobject flags */
273 	tmp32 &= ~(DUK_HOBJECT_FLAG_HAVE_FINALIZER);  /* finalizer flag is lost */
274 	DUK_RAW_WRITEINC_U32_BE(p, tmp32);
275 
276 	/* Bytecode instructions: endian conversion needed unless
277 	 * platform is big endian.
278 	 */
279 	ins = DUK_HCOMPFUNC_GET_CODE_BASE(thr->heap, func);
280 	ins_end = DUK_HCOMPFUNC_GET_CODE_END(thr->heap, func);
281 	DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr);
282 #if defined(DUK_USE_INTEGER_BE)
283 	duk_memcpy_unsafe((void *) p, (const void *) ins, (size_t) (ins_end - ins));
284 	p += (size_t) (ins_end - ins);
285 #else
286 	while (ins != ins_end) {
287 		tmp32 = (duk_uint32_t) (*ins);
288 		DUK_RAW_WRITEINC_U32_BE(p, tmp32);
289 		ins++;
290 	}
291 #endif
292 
293 	/* Constants: variable size encoding. */
294 	tv = DUK_HCOMPFUNC_GET_CONSTS_BASE(thr->heap, func);
295 	tv_end = DUK_HCOMPFUNC_GET_CONSTS_END(thr->heap, func);
296 	while (tv != tv_end) {
297 		/* constants are strings or numbers now */
298 		DUK_ASSERT(DUK_TVAL_IS_STRING(tv) ||
299 		           DUK_TVAL_IS_NUMBER(tv));
300 
301 		if (DUK_TVAL_IS_STRING(tv)) {
302 			h_str = DUK_TVAL_GET_STRING(tv);
303 			DUK_ASSERT(h_str != NULL);
304 			DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL);  /* ensures no overflow */
305 			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 4U + DUK_HSTRING_GET_BYTELEN(h_str), p);
306 			*p++ = DUK__SER_STRING;
307 			p = duk__dump_hstring_raw(p, h_str);
308 		} else {
309 			DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
310 			p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1U + 8U, p);
311 			*p++ = DUK__SER_NUMBER;
312 			d = DUK_TVAL_GET_NUMBER(tv);
313 			DUK_RAW_WRITEINC_DOUBLE_BE(p, d);
314 		}
315 		tv++;
316 	}
317 
318 	/* Inner functions recursively. */
319 	fn = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_BASE(thr->heap, func);
320 	fn_end = (duk_hobject **) DUK_HCOMPFUNC_GET_FUNCS_END(thr->heap, func);
321 	while (fn != fn_end) {
322 		/* XXX: This causes recursion up to inner function depth
323 		 * which is normally not an issue, e.g. mark-and-sweep uses
324 		 * a recursion limiter to avoid C stack issues.  Avoiding
325 		 * this would mean some sort of a work list or just refusing
326 		 * to serialize deep functions.
327 		 */
328 		DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC(*fn));
329 		p = duk__dump_func(thr, (duk_hcompfunc *) *fn, bw_ctx, p);
330 		fn++;
331 	}
332 
333 	/* Lexenv and varenv are not dumped. */
334 
335 	/* Object extra properties.
336 	 *
337 	 * There are some difference between function templates and functions.
338 	 * For example, function templates don't have .length and nargs is
339 	 * normally used to instantiate the functions.
340 	 */
341 
342 	p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs);
343 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
344 	p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME);
345 #endif
346 #if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
347 	p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME);
348 #endif
349 #if defined(DUK_USE_PC2LINE)
350 	p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE);
351 #endif
352 	p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func);
353 	p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func);
354 
355 	DUK_DD(DUK_DDPRINT("serialized function %p -> final pointer %p", (void *) func, (void *) p));
356 
357 	return p;
358 }
359 
360 /* Load a function from bytecode.  The function object returned here must
361  * match what is created by duk_js_push_closure() with respect to its flags,
362  * properties, etc.
363  *
364  * NOTE: there are intentionally no input buffer length / bound checks.
365  * Adding them would be easy but wouldn't ensure memory safety as untrusted
366  * or broken bytecode is unsafe during execution unless the opcodes themselves
367  * are validated (which is quite complex, especially for indirect opcodes).
368  */
369 
370 #define DUK__ASSERT_LEFT(n) do { \
371 		DUK_ASSERT((duk_size_t) (p_end - p) >= (duk_size_t) (n)); \
372 	} while (0)
373 
duk__load_func(duk_hthread * thr,const duk_uint8_t * p,const duk_uint8_t * p_end)374 static const duk_uint8_t *duk__load_func(duk_hthread *thr, const duk_uint8_t *p, const duk_uint8_t *p_end) {
375 	duk_hcompfunc *h_fun;
376 	duk_hbuffer *h_data;
377 	duk_size_t data_size;
378 	duk_uint32_t count_instr, count_const, count_funcs;
379 	duk_uint32_t n;
380 	duk_uint32_t tmp32;
381 	duk_small_uint_t const_type;
382 	duk_uint8_t *fun_data;
383 	duk_uint8_t *q;
384 	duk_idx_t idx_base;
385 	duk_tval *tv1;
386 	duk_uarridx_t arr_idx;
387 	duk_uarridx_t arr_limit;
388 	duk_hobject *func_env;
389 	duk_bool_t need_pop;
390 
391 	/* XXX: There's some overlap with duk_js_closure() here, but
392 	 * seems difficult to share code.  Ensure that the final function
393 	 * looks the same as created by duk_js_closure().
394 	 */
395 
396 	DUK_ASSERT(thr != NULL);
397 
398 	DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (const void *) p, (const void *) p_end));
399 
400 	DUK__ASSERT_LEFT(3 * 4);
401 	count_instr = DUK_RAW_READINC_U32_BE(p);
402 	count_const = DUK_RAW_READINC_U32_BE(p);
403 	count_funcs = DUK_RAW_READINC_U32_BE(p);
404 
405 	data_size = sizeof(duk_tval) * count_const +
406 	            sizeof(duk_hobject *) * count_funcs +
407 	            sizeof(duk_instr_t) * count_instr;
408 
409 	DUK_DD(DUK_DDPRINT("instr=%ld, const=%ld, funcs=%ld, data_size=%ld",
410 	                   (long) count_instr, (long) count_const,
411 	                   (long) count_const, (long) data_size));
412 
413 	/* Value stack is used to ensure reachability of constants and
414 	 * inner functions being loaded.  Require enough space to handle
415 	 * large functions correctly.
416 	 */
417 	duk_require_stack(thr, (duk_idx_t) (2 + count_const + count_funcs));
418 	idx_base = duk_get_top(thr);
419 
420 	/* Push function object, init flags etc.  This must match
421 	 * duk_js_push_closure() quite carefully.
422 	 */
423 	h_fun = duk_push_hcompfunc(thr);
424 	DUK_ASSERT(h_fun != NULL);
425 	DUK_ASSERT(DUK_HOBJECT_IS_COMPFUNC((duk_hobject *) h_fun));
426 	DUK_ASSERT(DUK_HCOMPFUNC_GET_DATA(thr->heap, h_fun) == NULL);
427 	DUK_ASSERT(DUK_HCOMPFUNC_GET_FUNCS(thr->heap, h_fun) == NULL);
428 	DUK_ASSERT(DUK_HCOMPFUNC_GET_BYTECODE(thr->heap, h_fun) == NULL);
429 	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
430 
431 	h_fun->nregs = DUK_RAW_READINC_U16_BE(p);
432 	h_fun->nargs = DUK_RAW_READINC_U16_BE(p);
433 #if defined(DUK_USE_DEBUGGER_SUPPORT)
434 	h_fun->start_line = DUK_RAW_READINC_U32_BE(p);
435 	h_fun->end_line = DUK_RAW_READINC_U32_BE(p);
436 #else
437 	p += 8;  /* skip line info */
438 #endif
439 
440 	/* duk_hcompfunc flags; quite version specific */
441 	tmp32 = DUK_RAW_READINC_U32_BE(p);
442 	DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32);  /* masks flags to only change duk_hobject flags */
443 
444 	/* standard prototype (no need to set here, already set) */
445 	DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_fun) == thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
446 #if 0
447 	DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
448 #endif
449 
450 	/* assert just a few critical flags */
451 	DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT);
452 	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&h_fun->obj));
453 	DUK_ASSERT(DUK_HOBJECT_HAS_COMPFUNC(&h_fun->obj));
454 	DUK_ASSERT(!DUK_HOBJECT_HAS_NATFUNC(&h_fun->obj));
455 	DUK_ASSERT(!DUK_HOBJECT_IS_THREAD(&h_fun->obj));
456 	DUK_ASSERT(!DUK_HOBJECT_IS_PROXY(&h_fun->obj));
457 	DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj));
458 	DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj));
459 	DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj));
460 
461 	/* Create function 'data' buffer but don't attach it yet. */
462 	fun_data = (duk_uint8_t *) duk_push_fixed_buffer_nozero(thr, data_size);
463 	DUK_ASSERT(fun_data != NULL);
464 
465 	/* Load bytecode instructions. */
466 	DUK_ASSERT(sizeof(duk_instr_t) == 4);
467 	DUK__ASSERT_LEFT(count_instr * sizeof(duk_instr_t));
468 #if defined(DUK_USE_INTEGER_BE)
469 	q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
470 	duk_memcpy((void *) q,
471 	           (const void *) p,
472 	           sizeof(duk_instr_t) * count_instr);
473 	p += sizeof(duk_instr_t) * count_instr;
474 #else
475 	q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
476 	for (n = count_instr; n > 0; n--) {
477 		*((duk_instr_t *) (void *) q) = DUK_RAW_READINC_U32_BE(p);
478 		q += sizeof(duk_instr_t);
479 	}
480 #endif
481 
482 	/* Load constants onto value stack but don't yet copy to buffer. */
483 	for (n = count_const; n > 0; n--) {
484 		DUK__ASSERT_LEFT(1);
485 		const_type = DUK_RAW_READINC_U8(p);
486 		switch (const_type) {
487 		case DUK__SER_STRING: {
488 			p = duk__load_string_raw(thr, p);
489 			break;
490 		}
491 		case DUK__SER_NUMBER: {
492 			/* Important to do a fastint check so that constants are
493 			 * properly read back as fastints.
494 			 */
495 			duk_tval tv_tmp;
496 			duk_double_t val;
497 			DUK__ASSERT_LEFT(8);
498 			val = DUK_RAW_READINC_DOUBLE_BE(p);
499 			DUK_TVAL_SET_NUMBER_CHKFAST_SLOW(&tv_tmp, val);
500 			duk_push_tval(thr, &tv_tmp);
501 			break;
502 		}
503 		default: {
504 			goto format_error;
505 		}
506 		}
507 	}
508 
509 	/* Load inner functions to value stack, but don't yet copy to buffer. */
510 	for (n = count_funcs; n > 0; n--) {
511 		p = duk__load_func(thr, p, p_end);
512 		if (p == NULL) {
513 			goto format_error;
514 		}
515 	}
516 
517 	/* With constants and inner functions on value stack, we can now
518 	 * atomically finish the function 'data' buffer, bump refcounts,
519 	 * etc.
520 	 *
521 	 * Here we take advantage of the value stack being just a duk_tval
522 	 * array: we can just memcpy() the constants as long as we incref
523 	 * them afterwards.
524 	 */
525 
526 	h_data = (duk_hbuffer *) duk_known_hbuffer(thr, idx_base + 1);
527 	DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data));
528 	DUK_HCOMPFUNC_SET_DATA(thr->heap, h_fun, h_data);
529 	DUK_HBUFFER_INCREF(thr, h_data);
530 
531 	tv1 = duk_get_tval(thr, idx_base + 2);  /* may be NULL if no constants or inner funcs */
532 	DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv1 != NULL);
533 
534 	q = fun_data;
535 	duk_memcpy_unsafe((void *) q, (const void *) tv1, sizeof(duk_tval) * count_const);
536 	for (n = count_const; n > 0; n--) {
537 		DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q);  /* no side effects */
538 		q += sizeof(duk_tval);
539 	}
540 	tv1 += count_const;
541 
542 	DUK_HCOMPFUNC_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q);
543 	for (n = count_funcs; n > 0; n--) {
544 		duk_hobject *h_obj;
545 
546 		DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv1));
547 		h_obj = DUK_TVAL_GET_OBJECT(tv1);
548 		DUK_ASSERT(h_obj != NULL);
549 		tv1++;
550 		DUK_HOBJECT_INCREF(thr, h_obj);
551 
552 		*((duk_hobject **) (void *) q) = h_obj;
553 		q += sizeof(duk_hobject *);
554 	}
555 
556 	DUK_HCOMPFUNC_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q);
557 
558 	/* The function object is now reachable and refcounts are fine,
559 	 * so we can pop off all the temporaries.
560 	 */
561 	DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(thr, idx_base)));
562 	duk_set_top(thr, idx_base + 1);
563 
564 	/* Setup function properties. */
565 	tmp32 = DUK_RAW_READINC_U32_BE(p);
566 	duk_push_u32(thr, tmp32);
567 	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_C);
568 
569 #if defined(DUK_USE_FUNC_NAME_PROPERTY)
570 	p = duk__load_string_raw(thr, p);  /* -> [ func funcname ] */
571 	func_env = thr->builtins[DUK_BIDX_GLOBAL_ENV];
572 	DUK_ASSERT(func_env != NULL);
573 	need_pop = 0;
574 	if (DUK_HOBJECT_HAS_NAMEBINDING((duk_hobject *) h_fun)) {
575 		/* Original function instance/template had NAMEBINDING.
576 		 * Must create a lexical environment on loading to allow
577 		 * recursive functions like 'function foo() { foo(); }'.
578 		 */
579 		duk_hdecenv *new_env;
580 
581 		new_env = duk_hdecenv_alloc(thr,
582 		                            DUK_HOBJECT_FLAG_EXTENSIBLE |
583 		                            DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV));
584 		DUK_ASSERT(new_env != NULL);
585 		DUK_ASSERT(new_env->thread == NULL);  /* Closed. */
586 		DUK_ASSERT(new_env->varmap == NULL);
587 		DUK_ASSERT(new_env->regbase_byteoff == 0);
588 		DUK_HDECENV_ASSERT_VALID(new_env);
589 		DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) new_env) == NULL);
590 		DUK_HOBJECT_SET_PROTOTYPE(thr->heap, (duk_hobject *) new_env, func_env);
591 		DUK_HOBJECT_INCREF(thr, func_env);
592 
593 		func_env = (duk_hobject *) new_env;
594 
595 		duk_push_hobject(thr, (duk_hobject *) new_env);
596 
597 		duk_dup_m2(thr);                                  /* -> [ func funcname env funcname ] */
598 		duk_dup(thr, idx_base);                           /* -> [ func funcname env funcname func ] */
599 		duk_xdef_prop(thr, -3, DUK_PROPDESC_FLAGS_NONE);  /* -> [ func funcname env ] */
600 
601 		need_pop = 1;  /* Need to pop env, but -after- updating h_fun and increfs. */
602 	}
603 	DUK_ASSERT(func_env != NULL);
604 	DUK_HCOMPFUNC_SET_LEXENV(thr->heap, h_fun, func_env);
605 	DUK_HCOMPFUNC_SET_VARENV(thr->heap, h_fun, func_env);
606 	DUK_HOBJECT_INCREF(thr, func_env);
607 	DUK_HOBJECT_INCREF(thr, func_env);
608 	if (need_pop) {
609 		duk_pop(thr);
610 	}
611 	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_C);
612 #endif  /* DUK_USE_FUNC_NAME_PROPERTY */
613 
614 #if defined(DUK_USE_FUNC_FILENAME_PROPERTY)
615 	p = duk__load_string_raw(thr, p);
616 	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_C);
617 #endif  /* DUK_USE_FUNC_FILENAME_PROPERTY */
618 
619 	if (DUK_HOBJECT_HAS_CONSTRUCTABLE((duk_hobject *) h_fun)) {
620 		/* Restore empty external .prototype only for constructable
621 		 * functions.  The prototype object should inherit from
622 		 * Object.prototype.
623 		 */
624 		duk_push_object(thr);
625 		DUK_ASSERT(!duk_is_bare_object(thr, -1));
626 		duk_dup_m2(thr);
627 		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC);  /* func.prototype.constructor = func */
628 		duk_compact_m1(thr);
629 		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);
630 	}
631 
632 #if defined(DUK_USE_PC2LINE)
633 	p = duk__load_buffer_raw(thr, p);
634 	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC);
635 #endif  /* DUK_USE_PC2LINE */
636 
637 	duk_push_bare_object(thr);  /* _Varmap */
638 	for (;;) {
639 		/* XXX: awkward */
640 		p = duk__load_string_raw(thr, p);
641 		if (duk_get_length(thr, -1) == 0) {
642 			duk_pop(thr);
643 			break;
644 		}
645 		tmp32 = DUK_RAW_READINC_U32_BE(p);
646 		duk_push_u32(thr, tmp32);
647 		duk_put_prop(thr, -3);
648 	}
649 	duk_compact_m1(thr);
650 	duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);
651 
652 	/* _Formals may have been missing in the original function, which is
653 	 * handled using a marker length.
654 	 */
655 	arr_limit = DUK_RAW_READINC_U32_BE(p);
656 	if (arr_limit != DUK__NO_FORMALS) {
657 		duk_push_bare_array(thr);  /* _Formals */
658 		for (arr_idx = 0; arr_idx < arr_limit; arr_idx++) {
659 			p = duk__load_string_raw(thr, p);
660 			duk_put_prop_index(thr, -2, arr_idx);
661 		}
662 		duk_compact_m1(thr);
663 		duk_xdef_prop_stridx_short(thr, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
664 	} else {
665 		DUK_DD(DUK_DDPRINT("no _Formals in dumped function"));
666 	}
667 
668 	/* Return with final function pushed on stack top. */
669 	DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(thr, -1)));
670 	DUK_ASSERT_TOP(thr, idx_base + 1);
671 	return p;
672 
673  format_error:
674 	return NULL;
675 }
676 
duk_dump_function(duk_hthread * thr)677 DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) {
678 	duk_hcompfunc *func;
679 	duk_bufwriter_ctx bw_ctx_alloc;
680 	duk_bufwriter_ctx *bw_ctx = &bw_ctx_alloc;
681 	duk_uint8_t *p;
682 
683 	DUK_ASSERT_API_ENTRY(thr);
684 
685 	/* Bound functions don't have all properties so we'd either need to
686 	 * lookup the non-bound target function or reject bound functions.
687 	 * For now, bound functions are rejected with TypeError.
688 	 */
689 	func = duk_require_hcompfunc(thr, -1);
690 	DUK_ASSERT(func != NULL);
691 	DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(&func->obj));
692 
693 	/* Estimating the result size beforehand would be costly, so
694 	 * start with a reasonable size and extend as needed.
695 	 */
696 	DUK_BW_INIT_PUSHBUF(thr, bw_ctx, DUK__BYTECODE_INITIAL_ALLOC);
697 	p = DUK_BW_GET_PTR(thr, bw_ctx);
698 	*p++ = DUK__SER_MARKER;
699 	p = duk__dump_func(thr, func, bw_ctx, p);
700 	DUK_BW_SET_PTR(thr, bw_ctx, p);
701 	DUK_BW_COMPACT(thr, bw_ctx);
702 
703 	DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(thr, -1)));
704 
705 	duk_remove_m2(thr);  /* [ ... func buf ] -> [ ... buf ] */
706 }
707 
duk_load_function(duk_hthread * thr)708 DUK_EXTERNAL void duk_load_function(duk_hthread *thr) {
709 	const duk_uint8_t *p_buf, *p, *p_end;
710 	duk_size_t sz;
711 
712 	DUK_ASSERT_API_ENTRY(thr);
713 
714 	p_buf = (duk_uint8_t *) duk_require_buffer(thr, -1, &sz);
715 	DUK_ASSERT(p_buf != NULL);
716 
717 	/* The caller is responsible for being sure that bytecode being loaded
718 	 * is valid and trusted.  Invalid bytecode can cause memory unsafe
719 	 * behavior directly during loading or later during bytecode execution
720 	 * (instruction validation would be quite complex to implement).
721 	 *
722 	 * This signature check is the only sanity check for detecting
723 	 * accidental invalid inputs.  The initial byte ensures no ordinary
724 	 * string or Symbol will be accepted by accident.
725 	 */
726 	p = p_buf;
727 	p_end = p_buf + sz;
728 	if (sz < 1 || p[0] != DUK__SER_MARKER) {
729 		goto format_error;
730 	}
731 	p++;
732 
733 	p = duk__load_func(thr, p, p_end);
734 	if (p == NULL) {
735 		goto format_error;
736 	}
737 
738 	duk_remove_m2(thr);  /* [ ... buf func ] -> [ ... func ] */
739 	return;
740 
741  format_error:
742 	DUK_ERROR_TYPE(thr, DUK_STR_INVALID_BYTECODE);
743 	DUK_WO_NORETURN(return;);
744 }
745 
746 #else  /* DUK_USE_BYTECODE_DUMP_SUPPORT */
747 
duk_dump_function(duk_hthread * thr)748 DUK_EXTERNAL void duk_dump_function(duk_hthread *thr) {
749 	DUK_ASSERT_API_ENTRY(thr);
750 	DUK_ERROR_UNSUPPORTED(thr);
751 	DUK_WO_NORETURN(return;);
752 }
753 
duk_load_function(duk_hthread * thr)754 DUK_EXTERNAL void duk_load_function(duk_hthread *thr) {
755 	DUK_ASSERT_API_ENTRY(thr);
756 	DUK_ERROR_UNSUPPORTED(thr);
757 	DUK_WO_NORETURN(return;);
758 }
759 
760 #endif  /* DUK_USE_BYTECODE_DUMP_SUPPORT */
761