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