]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.3.0/src-separate/duk_api_bytecode.c
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / src-separate / duk_api_bytecode.c
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 0xff
18 #define DUK__SER_VERSION 0x00
19 #define DUK__SER_STRING 0x00
20 #define DUK__SER_NUMBER 0x01
21 #define DUK__BYTECODE_INITIAL_ALLOC 256
22
23 /*
24 * Dump/load helpers, xxx_raw() helpers do no buffer checks
25 */
26
27 DUK_LOCAL duk_uint8_t *duk__load_string_raw(duk_context *ctx, duk_uint8_t *p) {
28 duk_uint32_t len;
29
30 len = DUK_RAW_READ_U32_BE(p);
31 duk_push_lstring(ctx, (const char *) p, len);
32 p += len;
33 return p;
34 }
35
36 DUK_LOCAL duk_uint8_t *duk__load_buffer_raw(duk_context *ctx, duk_uint8_t *p) {
37 duk_uint32_t len;
38 duk_uint8_t *buf;
39
40 len = DUK_RAW_READ_U32_BE(p);
41 buf = (duk_uint8_t *) duk_push_fixed_buffer(ctx, (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
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_WRITE_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
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_WRITE_U32_BE(p, tmp32);
77 DUK_MEMCPY((void *) p,
78 (const void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h),
79 len);
80 p += len;
81 return p;
82 }
83
84 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) {
85 duk_hstring *h_str;
86 duk_tval *tv;
87
88 tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx));
89 if (tv != NULL && DUK_TVAL_IS_STRING(tv)) {
90 h_str = DUK_TVAL_GET_STRING(tv);
91 DUK_ASSERT(h_str != NULL);
92 } else {
93 h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr);
94 DUK_ASSERT(h_str != NULL);
95 }
96 DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */
97 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(h_str), p);
98 p = duk__dump_hstring_raw(p, h_str);
99 return p;
100 }
101
102 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) {
103 duk_tval *tv;
104
105 tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx));
106 if (tv != NULL && DUK_TVAL_IS_BUFFER(tv)) {
107 duk_hbuffer *h_buf;
108 h_buf = DUK_TVAL_GET_BUFFER(tv);
109 DUK_ASSERT(h_buf != NULL);
110 DUK_ASSERT(DUK_HBUFFER_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */
111 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HBUFFER_GET_SIZE(h_buf), p);
112 p = duk__dump_hbuffer_raw(thr, p, h_buf);
113 } else {
114 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
115 DUK_RAW_WRITE_U32_BE(p, 0);
116 }
117 return p;
118 }
119
120 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) {
121 duk_tval *tv;
122 duk_uint32_t val;
123
124 tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_GET_STRING(thr, stridx));
125 if (tv != NULL && DUK_TVAL_IS_NUMBER(tv)) {
126 val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv);
127 } else {
128 val = def_value;
129 }
130 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
131 DUK_RAW_WRITE_U32_BE(p, val);
132 return p;
133 }
134
135 DUK_LOCAL duk_uint8_t *duk__dump_varmap(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) {
136 duk_tval *tv;
137
138 tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_VARMAP(thr));
139 if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) {
140 duk_hobject *h;
141 duk_uint_fast32_t i;
142
143 h = DUK_TVAL_GET_OBJECT(tv);
144 DUK_ASSERT(h != NULL);
145
146 /* We know _Varmap only has own properties so walk property
147 * table directly. We also know _Varmap is dense and all
148 * values are numbers; assert for these. GC and finalizers
149 * shouldn't affect _Varmap so side effects should be fine.
150 */
151 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h); i++) {
152 duk_hstring *key;
153 duk_tval *tv_val;
154 duk_uint32_t val;
155
156 key = DUK_HOBJECT_E_GET_KEY(thr->heap, h, i);
157 DUK_ASSERT(key != NULL); /* _Varmap is dense */
158 DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i));
159 tv_val = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i);
160 DUK_ASSERT(tv_val != NULL);
161 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_val)); /* known to be number; in fact an integer */
162 #if defined(DUK_USE_FASTINT)
163 DUK_ASSERT(DUK_TVAL_IS_FASTINT(tv_val));
164 DUK_ASSERT(DUK_TVAL_GET_FASTINT(tv_val) == (duk_int64_t) DUK_TVAL_GET_FASTINT_U32(tv_val)); /* known to be 32-bit */
165 val = DUK_TVAL_GET_FASTINT_U32(tv_val);
166 #else
167 val = (duk_uint32_t) DUK_TVAL_GET_NUMBER(tv_val);
168 #endif
169
170 DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */
171 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(key) + 4, p);
172 p = duk__dump_hstring_raw(p, key);
173 DUK_RAW_WRITE_U32_BE(p, val);
174 }
175 }
176 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
177 DUK_RAW_WRITE_U32_BE(p, 0); /* end of _Varmap */
178 return p;
179 }
180
181 DUK_LOCAL duk_uint8_t *duk__dump_formals(duk_hthread *thr, duk_uint8_t *p, duk_bufwriter_ctx *bw_ctx, duk_hobject *func) {
182 duk_tval *tv;
183
184 tv = duk_hobject_find_existing_entry_tval_ptr(thr->heap, (duk_hobject *) func, DUK_HTHREAD_STRING_INT_FORMALS(thr));
185 if (tv != NULL && DUK_TVAL_IS_OBJECT(tv)) {
186 duk_hobject *h;
187 duk_uint_fast32_t i;
188
189 h = DUK_TVAL_GET_OBJECT(tv);
190 DUK_ASSERT(h != NULL);
191
192 /* We know _Formals is dense and all entries will be in the
193 * array part. GC and finalizers shouldn't affect _Formals
194 * so side effects should be fine.
195 */
196 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) {
197 duk_tval *tv_val;
198 duk_hstring *varname;
199
200 tv_val = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i);
201 DUK_ASSERT(tv_val != NULL);
202 if (DUK_TVAL_IS_STRING(tv_val)) {
203 /* Array is dense and contains only strings, but ASIZE may
204 * be larger than used part and there are UNUSED entries.
205 */
206 varname = DUK_TVAL_GET_STRING(tv_val);
207 DUK_ASSERT(varname != NULL);
208
209 DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */
210 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4 + DUK_HSTRING_GET_BYTELEN(varname), p);
211 p = duk__dump_hstring_raw(p, varname);
212 }
213 }
214 }
215 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 4, p);
216 DUK_RAW_WRITE_U32_BE(p, 0); /* end of _Formals */
217 return p;
218 }
219
220 static duk_uint8_t *duk__dump_func(duk_context *ctx, duk_hcompiledfunction *func, duk_bufwriter_ctx *bw_ctx, duk_uint8_t *p) {
221 duk_hthread *thr;
222 duk_tval *tv, *tv_end;
223 duk_instr_t *ins, *ins_end;
224 duk_hobject **fn, **fn_end;
225 duk_hstring *h_str;
226 duk_uint32_t count_instr;
227 duk_uint32_t tmp32;
228 duk_uint16_t tmp16;
229 duk_double_t d;
230
231 thr = (duk_hthread *) ctx;
232 DUK_UNREF(ctx);
233 DUK_UNREF(thr);
234
235 DUK_DD(DUK_DDPRINT("dumping function %p to %p: "
236 "consts=[%p,%p[ (%ld bytes, %ld items), "
237 "funcs=[%p,%p[ (%ld bytes, %ld items), "
238 "code=[%p,%p[ (%ld bytes, %ld items)",
239 (void *) func,
240 (void *) p,
241 (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func),
242 (void *) DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func),
243 (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_SIZE(thr->heap, func),
244 (long) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func),
245 (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func),
246 (void *) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func),
247 (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_SIZE(thr->heap, func),
248 (long) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func),
249 (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func),
250 (void *) DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func),
251 (long) DUK_HCOMPILEDFUNCTION_GET_CODE_SIZE(thr->heap, func),
252 (long) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func)));
253
254 DUK_ASSERT(DUK_USE_ESBC_MAX_BYTES <= 0x7fffffffUL); /* ensures no overflow */
255 count_instr = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CODE_COUNT(thr->heap, func);
256 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 3 * 4 + 2 * 2 + 3 * 4 + count_instr * 4, p);
257
258 /* Fixed header info. */
259 tmp32 = count_instr;
260 DUK_RAW_WRITE_U32_BE(p, tmp32);
261 tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_CONSTS_COUNT(thr->heap, func);
262 DUK_RAW_WRITE_U32_BE(p, tmp32);
263 tmp32 = (duk_uint32_t) DUK_HCOMPILEDFUNCTION_GET_FUNCS_COUNT(thr->heap, func);
264 DUK_RAW_WRITE_U32_BE(p, tmp32);
265 tmp16 = func->nregs;
266 DUK_RAW_WRITE_U16_BE(p, tmp16);
267 tmp16 = func->nargs;
268 DUK_RAW_WRITE_U16_BE(p, tmp16);
269 #if defined(DUK_USE_DEBUGGER_SUPPORT)
270 tmp32 = func->start_line;
271 DUK_RAW_WRITE_U32_BE(p, tmp32);
272 tmp32 = func->end_line;
273 DUK_RAW_WRITE_U32_BE(p, tmp32);
274 #else
275 DUK_RAW_WRITE_U32_BE(p, 0);
276 DUK_RAW_WRITE_U32_BE(p, 0);
277 #endif
278 tmp32 = ((duk_heaphdr *) func)->h_flags & DUK_HEAPHDR_FLAGS_FLAG_MASK;
279 DUK_RAW_WRITE_U32_BE(p, tmp32);
280
281 /* Bytecode instructions: endian conversion needed unless
282 * platform is big endian.
283 */
284 ins = DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, func);
285 ins_end = DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, func);
286 DUK_ASSERT((duk_size_t) (ins_end - ins) == (duk_size_t) count_instr);
287 #if defined(DUK_USE_INTEGER_BE)
288 DUK_MEMCPY((void *) p, (const void *) ins, (size_t) (ins_end - ins));
289 p += (size_t) (ins_end - ins);
290 #else
291 while (ins != ins_end) {
292 tmp32 = (duk_uint32_t) (*ins);
293 DUK_RAW_WRITE_U32_BE(p, tmp32);
294 ins++;
295 }
296 #endif
297
298 /* Constants: variable size encoding. */
299 tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, func);
300 tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, func);
301 while (tv != tv_end) {
302 /* constants are strings or numbers now */
303 DUK_ASSERT(DUK_TVAL_IS_STRING(tv) ||
304 DUK_TVAL_IS_NUMBER(tv));
305
306 if (DUK_TVAL_IS_STRING(tv)) {
307 h_str = DUK_TVAL_GET_STRING(tv);
308 DUK_ASSERT(h_str != NULL);
309 DUK_ASSERT(DUK_HSTRING_MAX_BYTELEN <= 0x7fffffffUL); /* ensures no overflow */
310 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 4 + DUK_HSTRING_GET_BYTELEN(h_str), p),
311 *p++ = DUK__SER_STRING;
312 p = duk__dump_hstring_raw(p, h_str);
313 } else {
314 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
315 p = DUK_BW_ENSURE_RAW(thr, bw_ctx, 1 + 8, p);
316 *p++ = DUK__SER_NUMBER;
317 d = DUK_TVAL_GET_NUMBER(tv);
318 DUK_RAW_WRITE_DOUBLE_BE(p, d);
319 }
320 tv++;
321 }
322
323 /* Inner functions recursively. */
324 fn = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, func);
325 fn_end = (duk_hobject **) DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, func);
326 while (fn != fn_end) {
327 /* XXX: This causes recursion up to inner function depth
328 * which is normally not an issue, e.g. mark-and-sweep uses
329 * a recursion limiter to avoid C stack issues. Avoiding
330 * this would mean some sort of a work list or just refusing
331 * to serialize deep functions.
332 */
333 DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(*fn));
334 p = duk__dump_func(ctx, (duk_hcompiledfunction *) *fn, bw_ctx, p);
335 fn++;
336 }
337
338 /* Object extra properties.
339 *
340 * There are some difference between function templates and functions.
341 * For example, function templates don't have .length and nargs is
342 * normally used to instantiate the functions.
343 */
344
345 p = duk__dump_uint32_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_LENGTH, (duk_uint32_t) func->nargs);
346 p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_NAME);
347 p = duk__dump_string_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_FILE_NAME);
348 p = duk__dump_buffer_prop(thr, p, bw_ctx, (duk_hobject *) func, DUK_STRIDX_INT_PC2LINE);
349 p = duk__dump_varmap(thr, p, bw_ctx, (duk_hobject *) func);
350 p = duk__dump_formals(thr, p, bw_ctx, (duk_hobject *) func);
351
352 DUK_DD(DUK_DDPRINT("serialized function %p -> final pointer %p", (void *) func, (void *) p));
353
354 return p;
355 }
356
357 /* Load a function from bytecode. The function object returned here must
358 * match what is created by duk_js_push_closure() with respect to its flags,
359 * properties, etc.
360 *
361 * NOTE: there are intentionally no input buffer length / bound checks.
362 * Adding them would be easy but wouldn't ensure memory safety as untrusted
363 * or broken bytecode is unsafe during execution unless the opcodes themselves
364 * are validated (which is quite complex, especially for indirect opcodes).
365 */
366
367 #define DUK__ASSERT_LEFT(n) do { \
368 DUK_ASSERT((duk_size_t) (p_end - p) >= (duk_size_t) (n)); \
369 } while (0)
370
371 static duk_uint8_t *duk__load_func(duk_context *ctx, duk_uint8_t *p, duk_uint8_t *p_end) {
372 duk_hthread *thr;
373 duk_hcompiledfunction *h_fun;
374 duk_hbuffer *h_data;
375 duk_size_t data_size;
376 duk_uint32_t count_instr, count_const, count_funcs;
377 duk_uint32_t n;
378 duk_uint32_t tmp32;
379 duk_small_uint_t const_type;
380 duk_uint8_t *fun_data;
381 duk_uint8_t *q;
382 duk_idx_t idx_base;
383 duk_tval *tv1;
384 duk_uarridx_t arr_idx;
385
386 /* XXX: There's some overlap with duk_js_closure() here, but
387 * seems difficult to share code. Ensure that the final function
388 * looks the same as created by duk_js_closure().
389 */
390
391 DUK_ASSERT(ctx != NULL);
392 thr = (duk_hthread *) ctx;
393
394 DUK_DD(DUK_DDPRINT("loading function, p=%p, p_end=%p", (void *) p, (void *) p_end));
395
396 DUK__ASSERT_LEFT(3 * 4);
397 count_instr = DUK_RAW_READ_U32_BE(p);
398 count_const = DUK_RAW_READ_U32_BE(p);
399 count_funcs = DUK_RAW_READ_U32_BE(p);
400
401 data_size = sizeof(duk_tval) * count_const +
402 sizeof(duk_hobject *) * count_funcs +
403 sizeof(duk_instr_t) * count_instr;
404
405 DUK_DD(DUK_DDPRINT("instr=%ld, const=%ld, funcs=%ld, data_size=%ld",
406 (long) count_instr, (long) count_const,
407 (long) count_const, (long) data_size));
408
409 /* Value stack is used to ensure reachability of constants and
410 * inner functions being loaded. Require enough space to handle
411 * large functions correctly.
412 */
413 duk_require_stack(ctx, 2 + count_const + count_funcs);
414 idx_base = duk_get_top(ctx);
415
416 /* Push function object, init flags etc. This must match
417 * duk_js_push_closure() quite carefully.
418 */
419 duk_push_compiledfunction(ctx);
420 h_fun = duk_get_hcompiledfunction(ctx, -1);
421 DUK_ASSERT(h_fun != NULL);
422 DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION((duk_hobject *) h_fun));
423 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, h_fun) == NULL);
424 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_FUNCS(thr->heap, h_fun) == NULL);
425 DUK_ASSERT(DUK_HCOMPILEDFUNCTION_GET_BYTECODE(thr->heap, h_fun) == NULL);
426
427 h_fun->nregs = DUK_RAW_READ_U16_BE(p);
428 h_fun->nargs = DUK_RAW_READ_U16_BE(p);
429 #if defined(DUK_USE_DEBUGGER_SUPPORT)
430 h_fun->start_line = DUK_RAW_READ_U32_BE(p);
431 h_fun->end_line = DUK_RAW_READ_U32_BE(p);
432 #else
433 p += 8; /* skip line info */
434 #endif
435
436 /* duk_hcompiledfunction flags; quite version specific */
437 tmp32 = DUK_RAW_READ_U32_BE(p);
438 DUK_HEAPHDR_SET_FLAGS((duk_heaphdr *) h_fun, tmp32);
439
440 /* standard prototype */
441 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, &h_fun->obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
442
443 /* assert just a few critical flags */
444 DUK_ASSERT(DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) h_fun) == DUK_HTYPE_OBJECT);
445 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&h_fun->obj));
446 DUK_ASSERT(DUK_HOBJECT_HAS_COMPILEDFUNCTION(&h_fun->obj));
447 DUK_ASSERT(!DUK_HOBJECT_HAS_NATIVEFUNCTION(&h_fun->obj));
448 DUK_ASSERT(!DUK_HOBJECT_HAS_THREAD(&h_fun->obj));
449 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARRAY(&h_fun->obj));
450 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(&h_fun->obj));
451 DUK_ASSERT(!DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(&h_fun->obj));
452
453 /* Create function 'data' buffer but don't attach it yet. */
454 fun_data = (duk_uint8_t *) duk_push_fixed_buffer(ctx, data_size);
455 DUK_ASSERT(fun_data != NULL);
456
457 /* Load bytecode instructions. */
458 DUK_ASSERT(sizeof(duk_instr_t) == 4);
459 DUK__ASSERT_LEFT(count_instr * sizeof(duk_instr_t));
460 #if defined(DUK_USE_INTEGER_BE)
461 q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
462 DUK_MEMCPY((void *) q,
463 (const void *) p,
464 sizeof(duk_instr_t) * count_instr);
465 p += sizeof(duk_instr_t) * count_instr;
466 #else
467 q = fun_data + sizeof(duk_tval) * count_const + sizeof(duk_hobject *) * count_funcs;
468 for (n = count_instr; n > 0; n--) {
469 *((duk_instr_t *) (void *) q) = DUK_RAW_READ_U32_BE(p);
470 q += sizeof(duk_instr_t);
471 }
472 #endif
473
474 /* Load constants onto value stack but don't yet copy to buffer. */
475 for (n = count_const; n > 0; n--) {
476 DUK__ASSERT_LEFT(1);
477 const_type = DUK_RAW_READ_U8(p);
478 switch (const_type) {
479 case DUK__SER_STRING: {
480 p = duk__load_string_raw(ctx, p);
481 break;
482 }
483 case DUK__SER_NUMBER: {
484 /* Important to do a fastint check so that constants are
485 * properly read back as fastints.
486 */
487 duk_tval tv_tmp;
488 duk_double_t val;
489 DUK__ASSERT_LEFT(8);
490 val = DUK_RAW_READ_DOUBLE_BE(p);
491 DUK_TVAL_SET_NUMBER_CHKFAST(&tv_tmp, val);
492 duk_push_tval(ctx, &tv_tmp);
493 break;
494 }
495 default: {
496 goto format_error;
497 }
498 }
499 }
500
501 /* Load inner functions to value stack, but don't yet copy to buffer. */
502 for (n = count_funcs; n > 0; n--) {
503 p = duk__load_func(ctx, p, p_end);
504 if (p == NULL) {
505 goto format_error;
506 }
507 }
508
509 /* With constants and inner functions on value stack, we can now
510 * atomically finish the function 'data' buffer, bump refcounts,
511 * etc.
512 *
513 * Here we take advantage of the value stack being just a duk_tval
514 * array: we can just memcpy() the constants as long as we incref
515 * them afterwards.
516 */
517
518 h_data = (duk_hbuffer *) duk_get_hbuffer(ctx, idx_base + 1);
519 DUK_ASSERT(h_data != NULL);
520 DUK_ASSERT(!DUK_HBUFFER_HAS_DYNAMIC(h_data));
521 DUK_HCOMPILEDFUNCTION_SET_DATA(thr->heap, h_fun, h_data);
522 DUK_HBUFFER_INCREF(thr, h_data);
523
524 tv1 = duk_get_tval(ctx, idx_base + 2); /* may be NULL if no constants or inner funcs */
525 DUK_ASSERT((count_const == 0 && count_funcs == 0) || tv1 != NULL);
526
527 q = fun_data;
528 if (count_const > 0) {
529 /* Explicit zero size check to avoid NULL 'tv1'. */
530 DUK_MEMCPY((void *) q, (const void *) tv1, sizeof(duk_tval) * count_const);
531 for (n = count_const; n > 0; n--) {
532 DUK_TVAL_INCREF_FAST(thr, (duk_tval *) (void *) q); /* no side effects */
533 q += sizeof(duk_tval);
534 }
535 tv1 += count_const;
536 }
537
538 DUK_HCOMPILEDFUNCTION_SET_FUNCS(thr->heap, h_fun, (duk_hobject **) (void *) q);
539 for (n = count_funcs; n > 0; n--) {
540 duk_hobject *h_obj;
541
542 DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv1));
543 h_obj = DUK_TVAL_GET_OBJECT(tv1);
544 DUK_ASSERT(h_obj != NULL);
545 tv1++;
546 DUK_HOBJECT_INCREF(thr, h_obj);
547
548 *((duk_hobject **) (void *) q) = h_obj;
549 q += sizeof(duk_hobject *);
550 }
551
552 DUK_HCOMPILEDFUNCTION_SET_BYTECODE(thr->heap, h_fun, (duk_instr_t *) (void *) q);
553
554 /* The function object is now reachable and refcounts are fine,
555 * so we can pop off all the temporaries.
556 */
557 DUK_DDD(DUK_DDDPRINT("function is reachable, reset top; func: %!iT", duk_get_tval(ctx, idx_base)));
558 duk_set_top(ctx, idx_base + 1);
559
560 /* Setup function properties. */
561 tmp32 = DUK_RAW_READ_U32_BE(p);
562 duk_push_u32(ctx, tmp32);
563 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
564
565 p = duk__load_string_raw(ctx, p);
566 if (DUK_HOBJECT_HAS_NAMEBINDING((duk_hobject *) h_fun)) {
567 /* Original function instance/template had NAMEBINDING.
568 * Must create a lexical environment on loading to allow
569 * recursive functions like 'function foo() { foo(); }'.
570 */
571 duk_hobject *proto;
572
573 proto = thr->builtins[DUK_BIDX_GLOBAL_ENV];
574 (void) duk_push_object_helper_proto(ctx,
575 DUK_HOBJECT_FLAG_EXTENSIBLE |
576 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_DECENV),
577 proto);
578 duk_dup(ctx, -2); /* -> [ func funcname env funcname ] */
579 duk_dup(ctx, idx_base); /* -> [ func funcname env funcname func ] */
580 duk_xdef_prop(ctx, -3, DUK_PROPDESC_FLAGS_NONE); /* -> [ func funcname env ] */
581 duk_xdef_prop_stridx(ctx, idx_base, DUK_STRIDX_INT_LEXENV, DUK_PROPDESC_FLAGS_WC);
582 /* since closure has NEWENV, never define DUK_STRIDX_INT_VARENV, as it
583 * will be ignored anyway
584 */
585 }
586 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
587
588 p = duk__load_string_raw(ctx, p);
589 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_WC);
590
591 duk_push_object(ctx);
592 duk_dup(ctx, -2);
593 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_CONSTRUCTOR, DUK_PROPDESC_FLAGS_WC); /* func.prototype.constructor = func */
594 duk_compact(ctx, -1);
595 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_PROTOTYPE, DUK_PROPDESC_FLAGS_W);
596
597 p = duk__load_buffer_raw(ctx, p);
598 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_WC);
599
600 duk_push_object(ctx); /* _Varmap */
601 for (;;) {
602 /* XXX: awkward */
603 p = duk__load_string_raw(ctx, p);
604 if (duk_get_length(ctx, -1) == 0) {
605 duk_pop(ctx);
606 break;
607 }
608 tmp32 = DUK_RAW_READ_U32_BE(p);
609 duk_push_u32(ctx, tmp32);
610 duk_put_prop(ctx, -3);
611 }
612 duk_compact(ctx, -1);
613 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE);
614
615 duk_push_array(ctx); /* _Formals */
616 for (arr_idx = 0; ; arr_idx++) {
617 /* XXX: awkward */
618 p = duk__load_string_raw(ctx, p);
619 if (duk_get_length(ctx, -1) == 0) {
620 duk_pop(ctx);
621 break;
622 }
623 duk_put_prop_index(ctx, -2, arr_idx);
624 }
625 duk_compact(ctx, -1);
626 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE);
627
628 /* Return with final function pushed on stack top. */
629 DUK_DD(DUK_DDPRINT("final loaded function: %!iT", duk_get_tval(ctx, -1)));
630 DUK_ASSERT_TOP(ctx, idx_base + 1);
631 return p;
632
633 format_error:
634 return NULL;
635 }
636
637 DUK_EXTERNAL void duk_dump_function(duk_context *ctx) {
638 duk_hthread *thr;
639 duk_hcompiledfunction *func;
640 duk_bufwriter_ctx bw_ctx_alloc;
641 duk_bufwriter_ctx *bw_ctx = &bw_ctx_alloc;
642 duk_uint8_t *p;
643
644 DUK_ASSERT(ctx != NULL);
645 thr = (duk_hthread *) ctx;
646
647 /* Bound functions don't have all properties so we'd either need to
648 * lookup the non-bound target function or reject bound functions.
649 * For now, bound functions are rejected.
650 */
651 func = duk_require_hcompiledfunction(ctx, -1);
652 DUK_ASSERT(func != NULL);
653 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(&func->obj));
654
655 /* Estimating the result size beforehand would be costly, so
656 * start with a reasonable size and extend as needed.
657 */
658 DUK_BW_INIT_PUSHBUF(thr, bw_ctx, DUK__BYTECODE_INITIAL_ALLOC);
659 p = DUK_BW_GET_PTR(thr, bw_ctx);
660 *p++ = DUK__SER_MARKER;
661 *p++ = DUK__SER_VERSION;
662 p = duk__dump_func(ctx, func, bw_ctx, p);
663 DUK_BW_SET_PTR(thr, bw_ctx, p);
664 DUK_BW_COMPACT(thr, bw_ctx);
665
666 DUK_DD(DUK_DDPRINT("serialized result: %!T", duk_get_tval(ctx, -1)));
667
668 duk_remove(ctx, -2); /* [ ... func buf ] -> [ ... buf ] */
669 }
670
671 DUK_EXTERNAL void duk_load_function(duk_context *ctx) {
672 duk_hthread *thr;
673 duk_uint8_t *p_buf, *p, *p_end;
674 duk_size_t sz;
675
676 DUK_ASSERT(ctx != NULL);
677 thr = (duk_hthread *) ctx;
678 DUK_UNREF(ctx);
679
680 p_buf = (duk_uint8_t *) duk_require_buffer(ctx, -1, &sz);
681 DUK_ASSERT(p_buf != NULL);
682
683 /* The caller is responsible for being sure that bytecode being loaded
684 * is valid and trusted. Invalid bytecode can cause memory unsafe
685 * behavior directly during loading or later during bytecode execution
686 * (instruction validation would be quite complex to implement).
687 *
688 * This signature check is the only sanity check for detecting
689 * accidental invalid inputs. The initial 0xFF byte ensures no
690 * ordinary string will be accepted by accident.
691 */
692 p = p_buf;
693 p_end = p_buf + sz;
694 if (sz < 2 || p[0] != DUK__SER_MARKER || p[1] != DUK__SER_VERSION) {
695 goto format_error;
696 }
697 p += 2;
698
699 p = duk__load_func(ctx, p, p_end);
700 if (p == NULL) {
701 goto format_error;
702 }
703
704 duk_remove(ctx, -2); /* [ ... buf func ] -> [ ... func ] */
705 return;
706
707 format_error:
708 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_DECODE_FAILED);
709 }
710
711 #undef DUK__SER_MARKER
712 #undef DUK__SER_VERSION
713 #undef DUK__SER_STRING
714 #undef DUK__SER_NUMBER
715 #undef DUK__BYTECODE_INITIAL_ALLOC
716
717 #else /* DUK_USE_BYTECODE_DUMP_SUPPORT */
718
719 DUK_EXTERNAL void duk_dump_function(duk_context *ctx) {
720 DUK_ERROR((duk_hthread *) ctx, DUK_ERR_ERROR, DUK_STR_UNSUPPORTED);
721 }
722
723 DUK_EXTERNAL void duk_load_function(duk_context *ctx) {
724 DUK_ERROR((duk_hthread *) ctx, DUK_ERR_ERROR, DUK_STR_UNSUPPORTED);
725 }
726
727 #endif /* DUK_USE_BYTECODE_DUMP_SUPPORT */