· 6 years ago · Oct 13, 2019, 03:30 AM
1V7_PRIVATE enum v7_err b_exec(struct v7 *v7, const char *src, size_t src_len,
2 const char *filename, val_t func, val_t args,
3 val_t this_object, int is_json, int fr,
4 uint8_t is_constructor, val_t *res) {
5#if defined(V7_BCODE_TRACE_SRC)
6 fprintf(stderr, "src:'%s'\n", src);
7#endif
8
9/* TODO(mkm): use GC pool */
10#if !defined(V7_NO_COMPILER)
11 struct ast *a = (struct ast *) malloc(sizeof(struct ast));
12#endif
13 size_t saved_stack_len = v7->stack.len;
14 enum v7_err rcode = V7_OK;
15 val_t _res = V7_UNDEFINED;
16 struct gc_tmp_frame tf = new_tmp_frame(v7);
17 struct bcode *bcode = NULL;
18#if V7_ENABLE_STACK_TRACKING
19 struct stack_track_ctx stack_track_ctx;
20#endif
21 struct {
22 unsigned noopt : 1;
23 unsigned line_no_reset : 1;
24 } flags = {0, 0};
25
26 (void) filename;
27
28#if V7_ENABLE_STACK_TRACKING
29 v7_stack_track_start(v7, &stack_track_ctx);
30#endif
31
32 tmp_stack_push(&tf, &func);
33 tmp_stack_push(&tf, &args);
34 tmp_stack_push(&tf, &this_object);
35 tmp_stack_push(&tf, &_res);
36
37 /* init new bcode */
38 bcode = (struct bcode *) calloc(1, sizeof(*bcode));
39
40 bcode_init(bcode,
41#ifndef V7_FORCE_STRICT_MODE
42 0,
43#else
44 1,
45#endif
46#if !V7_DISABLE_FILENAMES
47 filename ? shdata_create_from_string(filename) : NULL,
48#else
49 NULL,
50#endif
51 0 /*filename not in ROM*/
52 );
53
54 retain_bcode(v7, bcode);
55 own_bcode(v7, bcode);
56
57#if !defined(V7_NO_COMPILER)
58 ast_init(a, 0);
59 a->refcnt = 1;
60#endif
61
62 if (src != NULL) {
63 /* Caller provided some source code, so, handle it somehow */
64
65 flags.line_no_reset = 1;
66
67 if (src_len >= sizeof(BIN_BCODE_SIGNATURE) &&
68 strncmp(BIN_BCODE_SIGNATURE, src, sizeof(BIN_BCODE_SIGNATURE)) == 0) {
69 /* we have a serialized bcode */
70
71 bcode_deserialize(v7, bcode, src + sizeof(BIN_BCODE_SIGNATURE));
72
73/*
74 * Currently, we only support serialized bcode that is stored in some
75 * mmapped memory. Otherwise, we don't yet have any mechanism to free
76 * this memory at the appropriate time.
77 */
78
79/*
80 * TODO(dfrank): currently, we remove this assert, and introduce memory
81 * leak. We need to support that properly.
82 */
83#if 0
84 assert(fr == 0);
85#else
86 if (fr) {
87 fr = 0;
88 }
89#endif
90 } else {
91/* Maybe regular JavaScript source or binary AST data */
92#if !defined(V7_NO_COMPILER)
93
94 if (src_len >= sizeof(BIN_AST_SIGNATURE) &&
95 strncmp(BIN_AST_SIGNATURE, src, sizeof(BIN_AST_SIGNATURE)) == 0) {
96 /* we have binary AST data */
97
98 if (fr == 0) {
99 /* Unmanaged memory, usually rom or mmapped flash */
100 mbuf_free(&a->mbuf);
101 a->mbuf.buf = (char *) (src + sizeof(BIN_AST_SIGNATURE));
102 a->mbuf.size = a->mbuf.len = src_len - sizeof(BIN_AST_SIGNATURE);
103 a->refcnt++; /* prevent freeing */
104 flags.noopt = 1;
105 } else {
106 mbuf_append(&a->mbuf, src + sizeof(BIN_AST_SIGNATURE),
107 src_len - sizeof(BIN_AST_SIGNATURE));
108 }
109 } else {
110 /* we have regular JavaScript source, so, parse it */
111 V7_TRY(parse(v7, a, src, src_len, is_json));
112 }
113
114 /* we now have binary AST, let's compile it */
115
116 if (!flags.noopt) {
117 ast_optimize(a);
118 }
119#if V7_ENABLE__Memory__stats
120 v7->function_arena_ast_size += a->mbuf.size;
121#endif
122
123 if (v7_is_undefined(this_object)) {
124 this_object = v7->vals.global_object;
125 }
126
127 if (!is_json) {
128 V7_TRY(compile_script(v7, a, bcode));
129 } else {
130 ast_off_t pos = 0;
131 V7_TRY(compile_expr(v7, a, &pos, bcode));
132 }
133#else /* V7_NO_COMPILER */
134 (void) is_json;
135 /* Parsing JavaScript code is disabled */
136 rcode = v7_throwf(v7, SYNTAX_ERROR,
137 "Parsing JS code is disabled by V7_NO_COMPILER");
138 V7_THROW(V7_SYNTAX_ERROR);
139#endif /* V7_NO_COMPILER */
140 }
141
142 } else if (is_js_function(func)) {
143 /*
144 * Caller did not provide source code, so, assume we should call
145 * provided function. Here, we prepare "wrapper" bcode.
146 */
147
148 struct bcode_builder bbuilder;
149 lit_t lit;
150 int args_cnt = v7_array_length(v7, args);
151
152 bcode_builder_init(v7, &bbuilder, bcode);
153
154 bcode_op(&bbuilder, OP_PUSH_UNDEFINED);
155
156 /* push `this` */
157 lit = bcode_add_lit(&bbuilder, this_object);
158 bcode_push_lit(&bbuilder, lit);
159
160 /* push func literal */
161 lit = bcode_add_lit(&bbuilder, func);
162 bcode_push_lit(&bbuilder, lit);
163
164 /* push args */
165 {
166 int i;
167 for (i = 0; i < args_cnt; i++) {
168 lit = bcode_add_lit(&bbuilder, v7_array_get(v7, args, i));
169 bcode_push_lit(&bbuilder, lit);
170 }
171 }
172
173 bcode_op(&bbuilder, OP_CALL);
174 /* TODO(dfrank): check if args <= 0x7f */
175 bcode_op(&bbuilder, (uint8_t) args_cnt);
176
177 bcode_op(&bbuilder, OP_SWAP_DROP);
178
179 bcode_builder_finalize(&bbuilder);
180 } else if (is_cfunction_lite(func) || is_cfunction_obj(v7, func)) {
181 /* call cfunction */
182
183 V7_TRY(call_cfunction(v7, func, this_object, args, is_constructor, &_res));
184
185 goto clean;
186 } else {
187 /* value is not a function */
188 V7_TRY(v7_throwf(v7, TYPE_ERROR, "value is not a function"));
189 }
190
191/* We now have bcode to evaluate; proceed to it */
192
193#if !defined(V7_NO_COMPILER)
194 /*
195 * Before we evaluate bcode, we can safely release AST since it's not needed
196 * anymore. Note that there's no leak here: if we `goto clean` from somewhere
197 * above, we'll anyway release the AST under `clean` as well.
198 */
199 release_ast(v7, a);
200 a = NULL;
201#endif /* V7_NO_COMPILER */
202
203 /* Evaluate bcode */
204 V7_TRY(eval_bcode(v7, bcode, this_object, flags.line_no_reset, &_res));
205
206clean:
207
208 /* free `src` if needed */
209 /*
210 * TODO(dfrank) : free it above, just after parsing, and make sure you use
211 * V7_TRY2() with custom label instead of V7_TRY()
212 */
213 if (src != NULL && fr) {
214 free((void *) src);
215 }
216
217 /* disown and release current bcode */
218 disown_bcode(v7, bcode);
219 release_bcode(v7, bcode);
220 bcode = NULL;
221
222 if (rcode != V7_OK) {
223 /* some exception happened. */
224 _res = v7->vals.thrown_error;
225
226 /*
227 * if this is a top-level bcode, clear thrown error from the v7 context
228 *
229 * TODO(dfrank): do we really need to do this?
230 *
231 * If we don't clear the error, then we should clear it manually after each
232 * call to v7_exec() or friends; otherwise, all the following calls will
233 * see this error.
234 *
235 * On the other hand, user would still need to clear the error if he calls
236 * v7_exec() from some cfunction. So, currently, sometimes we don't need
237 * to clear the error, and sometimes we do, which is confusing.
238 */
239 if (v7->act_bcodes.len == 0) {
240 v7->vals.thrown_error = V7_UNDEFINED;
241 v7->is_thrown = 0;
242 }
243 }
244
245 /*
246 * Data stack should have the same length as it was before evaluating script.
247 */
248 if (v7->stack.len != saved_stack_len) {
249 fprintf(stderr, "len=%d, saved=%d\n", (int) v7->stack.len,
250 (int) saved_stack_len);
251 }
252 assert(v7->stack.len == saved_stack_len);
253
254#if !defined(V7_NO_COMPILER)
255 /*
256 * release AST if needed (normally, it's already released above, before
257 * bcode evaluation)
258 */
259 if (a != NULL) {
260 release_ast(v7, a);
261 a = NULL;
262 }
263#endif /* V7_NO_COMPILER */
264
265 if (is_constructor && !v7_is_object(_res)) {
266 /* constructor returned non-object: replace it with `this` */
267 _res = v7_get_this(v7);
268 }
269
270 /* Provide the caller with the result, if asked to do so */
271 if (res != NULL) {
272 *res = _res;
273 }
274
275#if V7_ENABLE_STACK_TRACKING
276 {
277 int diff = v7_stack_track_end(v7, &stack_track_ctx);
278 if (diff > v7->stack_stat[V7_STACK_STAT_EXEC]) {
279 v7->stack_stat[V7_STACK_STAT_EXEC] = diff;
280 }
281 }
282#endif
283
284 tmp_frame_cleanup(&tf);
285 return rcode;
286}
287
288WARN_UNUSED_RESULT
289V7_PRIVATE enum v7_err b_apply(struct v7 *v7, v7_val_t func, v7_val_t this_obj,
290 v7_val_t args, uint8_t is_constructor,
291 v7_val_t *res) {
292 return b_exec(v7, NULL, 0, NULL, func, args, this_obj, 0, 0, is_constructor,
293 res);
294}
295#ifdef V7_MODULE_LINES
296#line 1 "v7/src/core.c"
297#endif
298/*
299 * Copyright (c) 2014 Cesanta Software Limited
300 * All rights reserved
301 */
302
303/* Amalgamated: #include "v7/builtin/builtin.h" */
304/* Amalgamated: #include "v7/src/internal.h" */
305/* Amalgamated: #include "v7/src/object.h" */
306/* Amalgamated: #include "v7/src/core.h" */
307/* Amalgamated: #include "v7/src/primitive.h" */
308/* Amalgamated: #include "v7/src/array.h" */
309/* Amalgamated: #include "v7/src/slre.h" */
310/* Amalgamated: #include "v7/src/bcode.h" */
311/* Amalgamated: #include "v7/src/stdlib.h" */
312/* Amalgamated: #include "v7/src/gc.h" */
313/* Amalgamated: #include "v7/src/heapusage.h" */
314/* Amalgamated: #include "v7/src/eval.h" */
315
316#ifdef V7_THAW
317extern struct v7_vals *fr_vals;
318#endif
319
320#ifdef HAS_V7_INFINITY
321double _v7_infinity;
322#endif
323
324#ifdef HAS_V7_NAN
325double _v7_nan;
326#endif
327
328#if defined(V7_CYG_PROFILE_ON)
329struct v7 *v7_head = NULL;
330#endif
331
332static void generic_object_destructor(struct v7 *v7, void *ptr) {
333 struct v7_generic_object *o = (struct v7_generic_object *) ptr;
334 struct v7_property *p;
335 struct mbuf *abuf;
336
337 /* TODO(mkm): make regexp use user data API */
338 p = v7_get_own_property2(v7, v7_object_to_value(&o->base), "", 0,
339 _V7_PROPERTY_HIDDEN);
340
341#if V7_ENABLE__RegExp
342 if (p != NULL && (p->value & V7_TAG_MASK) == V7_TAG_REGEXP) {
343 struct v7_regexp *rp = (struct v7_regexp *) get_ptr(p->value);
344 v7_disown(v7, &rp->regexp_string);
345 slre_free(rp->compiled_regexp);
346 free(rp);
347 }
348#endif
349
350 if (o->base.attributes & V7_OBJ_DENSE_ARRAY) {
351 if (p != NULL &&
352 ((abuf = (struct mbuf *) v7_get_ptr(v7, p->value)) != NULL)) {
353 mbuf_free(abuf);
354 free(abuf);
355 }
356 }
357
358 if (o->base.attributes & V7_OBJ_HAS_DESTRUCTOR) {
359 struct v7_property *p;
360 for (p = o->base.properties; p != NULL; p = p->next) {
361 if (p->attributes & _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR) {
362 if (v7_is_foreign(p->name)) {
363 v7_destructor_cb_t *cb =
364 (v7_destructor_cb_t *) v7_get_ptr(v7, p->name);
365 cb(v7, v7_get_ptr(v7, p->value));
366 }
367 break;
368 }
369 }
370 }
371
372#if V7_ENABLE_ENTITY_IDS
373 o->base.entity_id_base = V7_ENTITY_ID_PART_NONE;
374 o->base.entity_id_spec = V7_ENTITY_ID_PART_NONE;
375#endif
376}
377
378static void function_destructor(struct v7 *v7, void *ptr) {
379 struct v7_js_function *f = (struct v7_js_function *) ptr;
380 (void) v7;
381 if (f == NULL) return;
382
383 if (f->bcode != NULL) {
384 release_bcode(v7, f->bcode);
385 }
386
387#if V7_ENABLE_ENTITY_IDS
388 f->base.entity_id_base = V7_ENTITY_ID_PART_NONE;
389 f->base.entity_id_spec = V7_ENTITY_ID_PART_NONE;
390#endif
391}
392
393#if V7_ENABLE_ENTITY_IDS
394static void property_destructor(struct v7 *v7, void *ptr) {
395 struct v7_property *p = (struct v7_property *) ptr;
396 (void) v7;
397 if (p == NULL) return;
398
399 p->entity_id = V7_ENTITY_ID_NONE;
400}
401#endif
402
403struct v7 *v7_create(void) {
404 struct v7_create_opts opts;
405 memset(&opts, 0, sizeof(opts));
406 return v7_create_opt(opts);
407}
408
409struct v7 *v7_create_opt(struct v7_create_opts opts) {
410 struct v7 *v7 = NULL;
411 char z = 0;
412
413#if defined(HAS_V7_INFINITY) || defined(HAS_V7_NAN)
414 double zero = 0.0;
415#endif
416
417#ifdef HAS_V7_INFINITY
418 _v7_infinity = 1.0 / zero;
419#endif
420#ifdef HAS_V7_NAN
421 _v7_nan = zero / zero;
422#endif
423
424 if (opts.object_arena_size == 0) opts.object_arena_size = 200;
425 if (opts.function_arena_size == 0) opts.function_arena_size = 100;
426 if (opts.property_arena_size == 0) opts.property_arena_size = 400;
427
428 if ((v7 = (struct v7 *) calloc(1, sizeof(*v7))) != NULL) {
429#ifdef V7_STACK_SIZE
430 v7->sp_limit = (void *) ((uintptr_t) opts.c_stack_base - (V7_STACK_SIZE));
431 v7->sp_lwm = opts.c_stack_base;
432#ifdef V7_STACK_GUARD_MIN_SIZE
433 v7_sp_limit = v7->sp_limit;
434#endif
435#endif
436
437#if defined(V7_CYG_PROFILE_ON)
438 v7->next_v7 = v7_head;
439 v7_head = v7;
440#endif
441
442#if !V7_DISABLE_STR_ALLOC_SEQ
443 v7->gc_next_asn = 0;
444 v7->gc_min_asn = 0;
445#endif
446
447 v7->cur_dense_prop =
448 (struct v7_property *) calloc(1, sizeof(struct v7_property));
449 gc_arena_init(&v7->generic_object_arena, sizeof(struct v7_generic_object),
450 opts.object_arena_size, 10, "object");
451 v7->generic_object_arena.destructor = generic_object_destructor;
452 gc_arena_init(&v7->function_arena, sizeof(struct v7_js_function),
453 opts.function_arena_size, 10, "function");
454 v7->function_arena.destructor = function_destructor;
455 gc_arena_init(&v7->property_arena, sizeof(struct v7_property),
456 opts.property_arena_size, 10, "property");
457#if V7_ENABLE_ENTITY_IDS
458 v7->property_arena.destructor = property_destructor;
459#endif
460
461 /*
462 * The compacting GC exploits the null terminator of the previous
463 * string as marker.
464 */
465 mbuf_append(&v7->owned_strings, &z, 1);
466
467 v7->inhibit_gc = 1;
468 v7->vals.thrown_error = V7_UNDEFINED;
469
470 v7->call_stack = NULL;
471 v7->bottom_call_frame = NULL;
472
473#if defined(V7_THAW) && !defined(V7_FREEZE_NOT_READONLY)
474 {
475 struct v7_generic_object *obj;
476 v7->vals = *fr_vals;
477 v7->vals.global_object = v7_mk_object(v7);
478
479 /*
480 * The global object has to be mutable.
481 */
482 obj = get_generic_object_struct(v7->vals.global_object);
483 *obj = *get_generic_object_struct(fr_vals->global_object);
484 obj->base.attributes &= ~(V7_OBJ_NOT_EXTENSIBLE | V7_OBJ_OFF_HEAP);
485 v7_set(v7, v7->vals.global_object, "global", 6, v7->vals.global_object);
486 }
487#else
488 init_stdlib(v7);
489 init_file(v7);
490 init_crypto(v7);
491 init_socket(v7);
492#endif
493
494 v7->inhibit_gc = 0;
495 }
496
497 return v7;
498}
499
500val_t v7_get_global(struct v7 *v7) {
501 return v7->vals.global_object;
502}
503
504void v7_destroy(struct v7 *v7) {
505 if (v7 == NULL) return;
506 gc_arena_destroy(v7, &v7->generic_object_arena);
507 gc_arena_destroy(v7, &v7->function_arena);
508 gc_arena_destroy(v7, &v7->property_arena);
509
510 mbuf_free(&v7->owned_strings);
511 mbuf_free(&v7->owned_values);
512 mbuf_free(&v7->foreign_strings);
513 mbuf_free(&v7->json_visited_stack);
514 mbuf_free(&v7->tmp_stack);
515 mbuf_free(&v7->act_bcodes);
516 mbuf_free(&v7->stack);
517
518#if defined(V7_CYG_PROFILE_ON)
519 /* delete this v7 */
520 {
521 struct v7 *v, **prevp = &v7_head;
522 for (v = v7_head; v != NULL; prevp = &v->next_v7, v = v->next_v7) {
523 if (v == v7) {
524 *prevp = v->next_v7;
525 break;
526 }
527 }
528 }
529#endif
530
531 free(v7->cur_dense_prop);
532 free(v7);
533}
534
535v7_val_t v7_get_this(struct v7 *v7) {
536 /*
537 * By default, when there's no active call frame, will return Global Object
538 */
539 v7_val_t ret = v7->vals.global_object;
540
541 struct v7_call_frame_base *call_frame =
542 find_call_frame(v7, V7_CALL_FRAME_MASK_BCODE | V7_CALL_FRAME_MASK_CFUNC);
543
544 if (call_frame != NULL) {
545 if (call_frame->type_mask & V7_CALL_FRAME_MASK_BCODE) {
546 ret = ((struct v7_call_frame_bcode *) call_frame)->vals.this_obj;
547 } else if (call_frame->type_mask & V7_CALL_FRAME_MASK_CFUNC) {
548 ret = ((struct v7_call_frame_cfunc *) call_frame)->vals.this_obj;
549 } else {
550 assert(0);
551 }
552 }
553
554 return ret;
555}
556
557V7_PRIVATE v7_val_t get_scope(struct v7 *v7) {
558 struct v7_call_frame_private *call_frame =
559 (struct v7_call_frame_private *) find_call_frame(
560 v7, V7_CALL_FRAME_MASK_PRIVATE);
561
562 if (call_frame != NULL) {
563 return call_frame->vals.scope;
564 } else {
565 /* No active call frame, return global object */
566 return v7->vals.global_object;
567 }
568}
569
570V7_PRIVATE uint8_t is_strict_mode(struct v7 *v7) {
571 struct v7_call_frame_bcode *call_frame =
572 (struct v7_call_frame_bcode *) find_call_frame(v7,
573 V7_CALL_FRAME_MASK_BCODE);
574
575 if (call_frame != NULL) {
576 return call_frame->bcode->strict_mode;
577 } else {
578 /* No active call frame, assume no strict mode */
579 return 0;
580 }
581}
582
583v7_val_t v7_get_arguments(struct v7 *v7) {
584 return v7->vals.arguments;
585}
586
587v7_val_t v7_arg(struct v7 *v7, unsigned long n) {
588 return v7_array_get(v7, v7->vals.arguments, n);
589}
590
591unsigned long v7_argc(struct v7 *v7) {
592 return v7_array_length(v7, v7->vals.arguments);
593}
594
595void v7_own(struct v7 *v7, v7_val_t *v) {
596 heapusage_dont_count(1);
597 mbuf_append(&v7->owned_values, &v, sizeof(v));
598 heapusage_dont_count(0);
599}
600
601int v7_disown(struct v7 *v7, v7_val_t *v) {
602 v7_val_t **vp =
603 (v7_val_t **) (v7->owned_values.buf + v7->owned_values.len - sizeof(v));
604
605 for (; (char *) vp >= v7->owned_values.buf; vp--) {
606 if (*vp == v) {
607 *vp = *(v7_val_t **) (v7->owned_values.buf + v7->owned_values.len -
608 sizeof(v));
609 v7->owned_values.len -= sizeof(v);
610 return 1;
611 }
612 }
613
614 return 0;
615}
616
617void v7_set_gc_enabled(struct v7 *v7, int enabled) {
618 v7->inhibit_gc = !enabled;
619}
620
621void v7_interrupt(struct v7 *v7) {
622 v7->interrupted = 1;
623}
624
625const char *v7_get_parser_error(struct v7 *v7) {
626 return v7->error_msg;
627}
628
629#if V7_ENABLE_STACK_TRACKING
630
631int v7_stack_stat(struct v7 *v7, enum v7_stack_stat_what what) {
632 assert(what < V7_STACK_STATS_CNT);
633 return v7->stack_stat[what];
634}
635
636void v7_stack_stat_clean(struct v7 *v7) {
637 memset(v7->stack_stat, 0x00, sizeof(v7->stack_stat));
638}
639
640#endif /* V7_ENABLE_STACK_TRACKING */
641#ifdef V7_MODULE_LINES
642#line 1 "v7/src/primitive.c"
643#endif
644/*
645 * Copyright (c) 2014 Cesanta Software Limited
646 * All rights reserved
647 */
648
649/* Amalgamated: #include "v7/src/internal.h" */
650/* Amalgamated: #include "v7/src/core.h" */
651/* Amalgamated: #include "v7/src/primitive.h" */
652
653/* Number {{{ */
654
655NOINSTR static v7_val_t mk_number(double v) {
656 val_t res;
657 /* not every NaN is a JS NaN */
658 if (isnan(v)) {
659 res = V7_TAG_NAN;
660 } else {
661 union {
662 double d;
663 val_t r;
664 } u;
665 u.d = v;
666 res = u.r;
667 }
668 return res;
669}
670
671NOINSTR static double get_double(val_t v) {
672 union {
673 double d;
674 val_t v;
675 } u;
676 u.v = v;
677 /* Due to NaN packing, any non-numeric value is already a valid NaN value */
678 return u.d;
679}
680
681NOINSTR static v7_val_t mk_boolean(int v) {
682 return (!!v) | V7_TAG_BOOLEAN;
683}
684
685NOINSTR static int get_bool(val_t v) {
686 if (v7_is_boolean(v)) {
687 return v & 1;
688 } else {
689 return 0;
690 }
691}
692
693NOINSTR v7_val_t v7_mk_number(struct v7 *v7, double v) {
694 (void) v7;
695 return mk_number(v);
696}
697
698NOINSTR double v7_get_double(struct v7 *v7, v7_val_t v) {
699 (void) v7;
700 return get_double(v);
701}
702
703NOINSTR int v7_get_int(struct v7 *v7, v7_val_t v) {
704 (void) v7;
705 return (int) get_double(v);
706}
707
708int v7_is_number(val_t v) {
709 return v == V7_TAG_NAN || !isnan(get_double(v));
710}
711
712V7_PRIVATE int is_finite(struct v7 *v7, val_t v) {
713 return v7_is_number(v) && v != V7_TAG_NAN && !isinf(v7_get_double(v7, v));
714}
715
716/* }}} Number */
717
718/* Boolean {{{ */
719
720NOINSTR v7_val_t v7_mk_boolean(struct v7 *v7, int v) {
721 (void) v7;
722 return mk_boolean(v);
723}
724
725NOINSTR int v7_get_bool(struct v7 *v7, val_t v) {
726 (void) v7;
727 return get_bool(v);
728}
729
730int v7_is_boolean(val_t v) {
731 return (v & V7_TAG_MASK) == V7_TAG_BOOLEAN;
732}
733
734/* }}} Boolean */
735
736/* null {{{ */
737
738NOINSTR v7_val_t v7_mk_null(void) {
739 return V7_NULL;
740}
741
742int v7_is_null(val_t v) {
743 return v == V7_NULL;
744}
745
746/* }}} null */
747
748/* undefined {{{ */
749
750NOINSTR v7_val_t v7_mk_undefined(void) {
751 return V7_UNDEFINED;
752}
753
754int v7_is_undefined(val_t v) {
755 return v == V7_UNDEFINED;
756}
757
758/* }}} undefined */
759
760/* Foreign {{{ */
761
762V7_PRIVATE val_t pointer_to_value(void *p) {
763 uint64_t n = ((uint64_t)(uintptr_t) p);
764
765 assert((n & V7_TAG_MASK) == 0 || (n & V7_TAG_MASK) == (~0 & V7_TAG_MASK));
766 return n & ~V7_TAG_MASK;
767}
768
769V7_PRIVATE void *get_ptr(val_t v) {
770 return (void *) (uintptr_t)(v & 0xFFFFFFFFFFFFUL);
771}
772
773NOINSTR void *v7_get_ptr(struct v7 *v7, val_t v) {
774 (void) v7;
775 if (!v7_is_foreign(v)) {
776 return NULL;
777 }
778 return get_ptr(v);
779}
780
781NOINSTR v7_val_t v7_mk_foreign(struct v7 *v7, void *p) {
782 (void) v7;
783 return pointer_to_value(p) | V7_TAG_FOREIGN;
784}
785
786int v7_is_foreign(val_t v) {
787 return (v & V7_TAG_MASK) == V7_TAG_FOREIGN;
788}
789
790/* }}} Foreign */
791#ifdef V7_MODULE_LINES
792#line 1 "v7/src/function.c"
793#endif
794/*
795 * Copyright (c) 2014 Cesanta Software Limited
796 * All rights reserved
797 */
798
799/* Amalgamated: #include "v7/src/internal.h" */
800/* Amalgamated: #include "v7/src/primitive.h" */
801/* Amalgamated: #include "v7/src/core.h" */
802/* Amalgamated: #include "v7/src/function.h" */
803/* Amalgamated: #include "v7/src/gc.h" */
804/* Amalgamated: #include "v7/src/object.h" */
805
806static val_t js_function_to_value(struct v7_js_function *o) {
807 return pointer_to_value(o) | V7_TAG_FUNCTION;
808}
809
810V7_PRIVATE struct v7_js_function *get_js_function_struct(val_t v) {
811 struct v7_js_function *ret = NULL;
812 assert(is_js_function(v));
813 ret = (struct v7_js_function *) get_ptr(v);
814#if V7_ENABLE_ENTITY_IDS
815 if (ret->base.entity_id_spec != V7_ENTITY_ID_PART_JS_FUNC) {
816 fprintf(stderr, "entity_id: not a function!\n");
817 abort();
818 } else if (ret->base.entity_id_base != V7_ENTITY_ID_PART_OBJ) {
819 fprintf(stderr, "entity_id: not an object!\n");
820 abort();
821 }
822#endif
823 return ret;
824}
825
826V7_PRIVATE
827val_t mk_js_function(struct v7 *v7, struct v7_generic_object *scope,
828 val_t proto) {
829 struct v7_js_function *f;
830 val_t fval = V7_NULL;
831 struct gc_tmp_frame tf = new_tmp_frame(v7);
832 tmp_stack_push(&tf, &proto);
833 tmp_stack_push(&tf, &fval);
834
835 f = new_function(v7);
836
837 if (f == NULL) {
838 /* fval is left `null` */
839 goto cleanup;
840 }
841
842#if V7_ENABLE_ENTITY_IDS
843 f->base.entity_id_base = V7_ENTITY_ID_PART_OBJ;
844 f->base.entity_id_spec = V7_ENTITY_ID_PART_JS_FUNC;
845#endif
846
847 fval = js_function_to_value(f);
848
849 f->base.properties = NULL;
850 f->scope = scope;
851
852 /*
853 * Before setting a `V7_OBJ_FUNCTION` flag, make sure we don't have
854 * `V7_OBJ_DENSE_ARRAY` flag set
855 */
856 assert(!(f->base.attributes & V7_OBJ_DENSE_ARRAY));
857 f->base.attributes |= V7_OBJ_FUNCTION;
858
859 /* TODO(mkm): lazily create these properties on first access */
860 if (v7_is_object(proto)) {
861 v7_def(v7, proto, "constructor", 11, V7_DESC_ENUMERABLE(0), fval);
862 v7_def(v7, fval, "prototype", 9,
863 V7_DESC_ENUMERABLE(0) | V7_DESC_CONFIGURABLE(0), proto);
864 }
865
866cleanup:
867 tmp_frame_cleanup(&tf);
868 return fval;
869}
870
871V7_PRIVATE int is_js_function(val_t v) {
872 return (v & V7_TAG_MASK) == V7_TAG_FUNCTION;
873}
874
875V7_PRIVATE
876v7_val_t mk_cfunction_obj(struct v7 *v7, v7_cfunction_t *f, int num_args) {
877 val_t obj = mk_object(v7, v7->vals.function_prototype);
878 struct gc_tmp_frame tf = new_tmp_frame(v7);
879 tmp_stack_push(&tf, &obj);
880 v7_def(v7, obj, "", 0, _V7_DESC_HIDDEN(1), v7_mk_cfunction(f));
881 if (num_args >= 0) {
882 v7_def(v7, obj, "length", 6, (V7_DESC_ENUMERABLE(0) | V7_DESC_WRITABLE(0) |
883 V7_DESC_CONFIGURABLE(0)),
884 v7_mk_number(v7, num_args));
885 }
886 tmp_frame_cleanup(&tf);
887 return obj;
888}
889
890V7_PRIVATE v7_val_t mk_cfunction_obj_with_proto(struct v7 *v7,
891 v7_cfunction_t *f, int num_args,
892 v7_val_t proto) {
893 struct gc_tmp_frame tf = new_tmp_frame(v7);
894 v7_val_t res = mk_cfunction_obj(v7, f, num_args);
895
896 tmp_stack_push(&tf, &res);
897
898 v7_def(v7, res, "prototype", 9, (V7_DESC_ENUMERABLE(0) | V7_DESC_WRITABLE(0) |
899 V7_DESC_CONFIGURABLE(0)),
900 proto);
901 v7_def(v7, proto, "constructor", 11, V7_DESC_ENUMERABLE(0), res);
902 tmp_frame_cleanup(&tf);
903 return res;
904}
905
906V7_PRIVATE v7_val_t mk_cfunction_lite(v7_cfunction_t *f) {
907 union {
908 void *p;
909 v7_cfunction_t *f;
910 } u;
911 u.f = f;
912 return pointer_to_value(u.p) | V7_TAG_CFUNCTION;
913}
914
915V7_PRIVATE v7_cfunction_t *get_cfunction_ptr(struct v7 *v7, val_t v) {
916 v7_cfunction_t *ret = NULL;
917
918 if (is_cfunction_lite(v)) {
919 /* Implementation is identical to get_ptr but is separate since
920 * object pointers are not directly convertible to function pointers
921 * according to ISO C and generates a warning in -Wpedantic mode. */
922 ret = (v7_cfunction_t *) (uintptr_t)(v & 0xFFFFFFFFFFFFUL);
923 } else {
924 /* maybe cfunction object */
925
926 /* extract the hidden property from a cfunction_object */
927 struct v7_property *p;
928 p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
929 if (p != NULL) {
930 /* yes, it's cfunction object. Extract cfunction pointer from it */
931 ret = get_cfunction_ptr(v7, p->value);
932 }
933 }
934
935 return ret;
936}
937
938V7_PRIVATE int is_cfunction_lite(val_t v) {
939 return (v & V7_TAG_MASK) == V7_TAG_CFUNCTION;
940}
941
942V7_PRIVATE int is_cfunction_obj(struct v7 *v7, val_t v) {
943 int ret = 0;
944 if (v7_is_object(v)) {
945 /* extract the hidden property from a cfunction_object */
946 struct v7_property *p;
947 p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
948 if (p != NULL) {
949 v = p->value;
950 }
951
952 ret = is_cfunction_lite(v);
953 }
954 return ret;
955}
956
957v7_val_t v7_mk_function(struct v7 *v7, v7_cfunction_t *f) {
958 return mk_cfunction_obj(v7, f, -1);
959}
960
961v7_val_t v7_mk_function_with_proto(struct v7 *v7, v7_cfunction_t *f,
962 v7_val_t proto) {
963 return mk_cfunction_obj_with_proto(v7, f, ~0, proto);
964}
965
966v7_val_t v7_mk_cfunction(v7_cfunction_t *f) {
967 return mk_cfunction_lite(f);
968}
969
970int v7_is_callable(struct v7 *v7, val_t v) {
971 return is_js_function(v) || is_cfunction_lite(v) || is_cfunction_obj(v7, v);
972}
973#ifdef V7_MODULE_LINES
974#line 1 "v7/src/exec.c"
975#endif
976/*
977 * Copyright (c) 2014 Cesanta Software Limited
978 * All rights reserved
979 */
980
981/* osdep.h must be included before `cs_file.h` TODO(dfrank) : fix this */
982/* Amalgamated: #include "common/cs_file.h" */
983/* Amalgamated: #include "v7/src/internal.h" */
984/* Amalgamated: #include "v7/src/core.h" */
985/* Amalgamated: #include "v7/src/eval.h" */
986/* Amalgamated: #include "v7/src/exec.h" */
987/* Amalgamated: #include "v7/src/ast.h" */
988/* Amalgamated: #include "v7/src/compiler.h" */
989/* Amalgamated: #include "v7/src/exceptions.h" */
990
991enum v7_err v7_exec(struct v7 *v7, const char *js_code, v7_val_t *res) {
992 return b_exec(v7, js_code, strlen(js_code), NULL, V7_UNDEFINED, V7_UNDEFINED,
993 V7_UNDEFINED, 0, 0, 0, res);
994}
995
996enum v7_err v7_exec_opt(struct v7 *v7, const char *js_code,
997 const struct v7_exec_opts *opts, v7_val_t *res) {
998 return b_exec(v7, js_code, strlen(js_code), opts->filename, V7_UNDEFINED,
999 V7_UNDEFINED,
1000 (opts->this_obj == 0 ? V7_UNDEFINED : opts->this_obj),
1001 opts->is_json, 0, 0, res);
1002}
1003
1004enum v7_err v7_exec_buf(struct v7 *v7, const char *js_code, size_t len,
1005 v7_val_t *res) {
1006 return b_exec(v7, js_code, len, NULL, V7_UNDEFINED, V7_UNDEFINED,
1007 V7_UNDEFINED, 0, 0, 0, res);
1008}
1009
1010enum v7_err v7_parse_json(struct v7 *v7, const char *str, v7_val_t *res) {
1011 return b_exec(v7, str, strlen(str), NULL, V7_UNDEFINED, V7_UNDEFINED,
1012 V7_UNDEFINED, 1, 0, 0, res);
1013}
1014
1015#ifndef V7_NO_FS
1016static enum v7_err exec_file(struct v7 *v7, const char *path, val_t *res,
1017 int is_json) {
1018 enum v7_err rcode = V7_OK;
1019 char *p;
1020 size_t file_size;
1021 char *(*rd)(const char *, size_t *);
1022
1023 rd = cs_read_file;
1024#ifdef V7_MMAP_EXEC
1025 rd = cs_mmap_file;
1026#ifdef V7_MMAP_EXEC_ONLY
1027#define I_STRINGIFY(x) #x
1028#define I_STRINGIFY2(x) I_STRINGIFY(x)
1029
1030 /* use mmap only for .js files */
1031 if (strlen(path) <= 3 || strcmp(path + strlen(path) - 3, ".js") != 0) {
1032 rd = cs_read_file;
1033 }
1034#endif
1035#endif
1036
1037 if ((p = rd(path, &file_size)) == NULL) {
1038 rcode = v7_throwf(v7, SYNTAX_ERROR, "cannot open [%s]", path);
1039 /*
1040 * In order to maintain compat with existing API, we should save the
1041 * current exception value into `*res`
1042 *
1043 * TODO(dfrank): probably change API: clients can use
1044 *`v7_get_thrown_value()` now.
1045 */
1046 if (res != NULL) *res = v7_get_thrown_value(v7, NULL);
1047 goto clean;
1048 } else {
1049#ifndef V7_MMAP_EXEC
1050 int fr = 1;
1051#else
1052 int fr = 0;
1053#endif
1054 rcode = b_exec(v7, p, file_size, path, V7_UNDEFINED, V7_UNDEFINED,
1055 V7_UNDEFINED, is_json, fr, 0, res);
1056 if (rcode != V7_OK) {
1057 goto clean;
1058 }
1059 }
1060
1061clean:
1062 return rcode;
1063}
1064
1065enum v7_err v7_exec_file(struct v7 *v7, const char *path, val_t *res) {
1066 return exec_file(v7, path, res, 0);
1067}
1068
1069enum v7_err v7_parse_json_file(struct v7 *v7, const char *path, v7_val_t *res) {
1070 return exec_file(v7, path, res, 1);
1071}
1072#endif /* V7_NO_FS */
1073
1074enum v7_err v7_apply(struct v7 *v7, v7_val_t func, v7_val_t this_obj,
1075 v7_val_t args, v7_val_t *res) {
1076 return b_apply(v7, func, this_obj, args, 0, res);
1077}
1078
1079#ifndef NO_LIBC
1080#if !defined(V7_NO_COMPILER)
1081enum v7_err _v7_compile(const char *src, size_t js_code_size, int binary,
1082 int use_bcode, FILE *fp) {
1083 struct ast ast;
1084 struct v7 *v7 = v7_create();
1085 ast_off_t pos = 0;
1086 enum v7_err err;
1087
1088 v7->is_precompiling = 1;
1089
1090 ast_init(&ast, 0);
1091 err = parse(v7, &ast, src, js_code_size, 0);
1092 if (err == V7_OK) {
1093 if (use_bcode) {
1094 struct bcode bcode;
1095 /*
1096 * We don't set filename here, because the bcode will be just serialized
1097 * and then freed. We don't currently serialize filename. If we ever do,
1098 * we'll have to make `_v7_compile()` to also take a filename argument,
1099 * and use it here.
1100 */
1101 bcode_init(&bcode, 0, NULL, 0);
1102 err = compile_script(v7, &ast, &bcode);
1103 if (err != V7_OK) {
1104 goto cleanup_bcode;
1105 }
1106
1107 if (binary) {
1108 bcode_serialize(v7, &bcode, fp);
1109 } else {
1110#ifdef V7_BCODE_DUMP
1111 dump_bcode(v7, fp, &bcode);
1112#else
1113 fprintf(stderr, "build flag V7_BCODE_DUMP not enabled\n");
1114#endif
1115 }
1116 cleanup_bcode:
1117 bcode_free(v7, &bcode);
1118 } else {
1119 if (binary) {
1120 fwrite(BIN_AST_SIGNATURE, sizeof(BIN_AST_SIGNATURE), 1, fp);
1121 fwrite(ast.mbuf.buf, ast.mbuf.len, 1, fp);
1122 } else {
1123 ast_dump_tree(fp, &ast, &pos, 0);
1124 }
1125 }
1126 }
1127
1128 ast_free(&ast);
1129 v7_destroy(v7);
1130 return err;
1131}
1132
1133enum v7_err v7_compile(const char *src, int binary, int use_bcode, FILE *fp) {
1134 return _v7_compile(src, strlen(src), binary, use_bcode, fp);
1135}
1136#endif /* V7_NO_COMPILER */
1137#endif
1138#ifdef V7_MODULE_LINES
1139#line 1 "v7/src/util.c"
1140#endif
1141/*
1142 * Copyright (c) 2014 Cesanta Software Limited
1143 * All rights reserved
1144 */
1145
1146/* Amalgamated: #include "v7/src/internal.h" */
1147/* Amalgamated: #include "v7/src/core.h" */
1148/* Amalgamated: #include "v7/src/object.h" */
1149/* Amalgamated: #include "v7/src/util.h" */
1150/* Amalgamated: #include "v7/src/string.h" */
1151/* Amalgamated: #include "v7/src/array.h" */
1152/* Amalgamated: #include "v7/src/eval.h" */
1153/* Amalgamated: #include "v7/src/conversion.h" */
1154/* Amalgamated: #include "v7/src/exceptions.h" */
1155/* Amalgamated: #include "v7/src/primitive.h" */
1156/* Amalgamated: #include "v7/src/std_proxy.h" */
1157
1158void v7_print(struct v7 *v7, v7_val_t v) {
1159 v7_fprint(stdout, v7, v);
1160}
1161
1162void v7_fprint(FILE *f, struct v7 *v7, val_t v) {
1163 char buf[16];
1164 char *s = v7_stringify(v7, v, buf, sizeof(buf), V7_STRINGIFY_DEBUG);
1165 fprintf(f, "%s", s);
1166 if (buf != s) free(s);
1167}
1168
1169void v7_println(struct v7 *v7, v7_val_t v) {
1170 v7_fprintln(stdout, v7, v);
1171}
1172
1173void v7_fprintln(FILE *f, struct v7 *v7, val_t v) {
1174 v7_fprint(f, v7, v);
1175 fprintf(f, ENDL);
1176}
1177
1178void v7_fprint_stack_trace(FILE *f, struct v7 *v7, val_t e) {
1179 size_t s;
1180 val_t strace_v = v7_get(v7, e, "stack", ~0);
1181 const char *strace = NULL;
1182 if (v7_is_string(strace_v)) {
1183 strace = v7_get_string(v7, &strace_v, &s);
1184 fprintf(f, "%s\n", strace);
1185 }
1186}
1187
1188void v7_print_error(FILE *f, struct v7 *v7, const char *ctx, val_t e) {
1189 /* TODO(mkm): figure out if this is an error object and which kind */
1190 v7_val_t msg;
1191 if (v7_is_undefined(e)) {
1192 fprintf(f, "undefined error [%s]\n ", ctx);
1193 return;
1194 }
1195 msg = v7_get(v7, e, "message", ~0);
1196 if (v7_is_undefined(msg)) {
1197 msg = e;
1198 }
1199 fprintf(f, "Exec error [%s]: ", ctx);
1200 v7_fprintln(f, v7, msg);
1201 v7_fprint_stack_trace(f, v7, e);
1202}
1203
1204#if V7_ENABLE__Proxy
1205
1206v7_val_t v7_mk_proxy(struct v7 *v7, v7_val_t target,
1207 const v7_proxy_hnd_t *handler) {
1208 enum v7_err rcode = V7_OK;
1209 v7_val_t res = V7_UNDEFINED;
1210 v7_val_t args = V7_UNDEFINED;
1211 v7_val_t handler_v = V7_UNDEFINED;
1212
1213 v7_own(v7, &res);
1214 v7_own(v7, &args);
1215 v7_own(v7, &handler_v);
1216 v7_own(v7, &target);
1217
1218 /* if target is not an object, create one */
1219 if (!v7_is_object(target)) {
1220 target = v7_mk_object(v7);
1221 }
1222
1223 /* prepare handler object with necessary properties */
1224 handler_v = v7_mk_object(v7);
1225 if (handler->get != NULL) {
1226 set_cfunc_prop(v7, handler_v, "get", handler->get);
1227 }
1228 if (handler->set != NULL) {
1229 set_cfunc_prop(v7, handler_v, "set", handler->set);
1230 }
1231 if (handler->own_keys != NULL) {
1232 set_cfunc_prop(v7, handler_v, "ownKeys", handler->own_keys);
1233 }
1234 if (handler->get_own_prop_desc != NULL) {
1235 v7_def(v7, handler_v, "_gpdc", ~0, V7_DESC_ENUMERABLE(0),
1236 v7_mk_foreign(v7, (void *) handler->get_own_prop_desc));
1237 }
1238
1239 /* prepare args */
1240 args = v7_mk_dense_array(v7);
1241 v7_array_set(v7, args, 0, target);
1242 v7_array_set(v7, args, 1, handler_v);
1243
1244 /* call Proxy constructor */
1245 V7_TRY(b_apply(v7, v7_get(v7, v7->vals.global_object, "Proxy", ~0),
1246 v7_mk_object(v7), args, 1 /* as ctor */, &res));
1247
1248clean:
1249 if (rcode != V7_OK) {
1250 fprintf(stderr, "error during v7_mk_proxy()");
1251 res = V7_UNDEFINED;
1252 }
1253
1254 v7_disown(v7, &target);
1255 v7_disown(v7, &handler_v);
1256 v7_disown(v7, &args);
1257 v7_disown(v7, &res);
1258 return res;
1259}
1260
1261#endif /* V7_ENABLE__Proxy */
1262
1263V7_PRIVATE enum v7_type val_type(struct v7 *v7, val_t v) {
1264 int tag;
1265 if (v7_is_number(v)) {
1266 return V7_TYPE_NUMBER;
1267 }
1268 tag = (v & V7_TAG_MASK) >> 48;
1269 switch (tag) {
1270 case V7_TAG_FOREIGN >> 48:
1271 if (v7_is_null(v)) {
1272 return V7_TYPE_NULL;
1273 }
1274 return V7_TYPE_FOREIGN;
1275 case V7_TAG_UNDEFINED >> 48:
1276 return V7_TYPE_UNDEFINED;
1277 case V7_TAG_OBJECT >> 48:
1278 if (v7_get_proto(v7, v) == v7->vals.array_prototype) {
1279 return V7_TYPE_ARRAY_OBJECT;
1280 } else if (v7_get_proto(v7, v) == v7->vals.boolean_prototype) {
1281 return V7_TYPE_BOOLEAN_OBJECT;
1282 } else if (v7_get_proto(v7, v) == v7->vals.string_prototype) {
1283 return V7_TYPE_STRING_OBJECT;
1284 } else if (v7_get_proto(v7, v) == v7->vals.number_prototype) {
1285 return V7_TYPE_NUMBER_OBJECT;
1286 } else if (v7_get_proto(v7, v) == v7->vals.function_prototype) {
1287 return V7_TYPE_CFUNCTION_OBJECT;
1288 } else if (v7_get_proto(v7, v) == v7->vals.date_prototype) {
1289 return V7_TYPE_DATE_OBJECT;
1290 } else {
1291 return V7_TYPE_GENERIC_OBJECT;
1292 }
1293 case V7_TAG_STRING_I >> 48:
1294 case V7_TAG_STRING_O >> 48:
1295 case V7_TAG_STRING_F >> 48:
1296 case V7_TAG_STRING_D >> 48:
1297 case V7_TAG_STRING_5 >> 48:
1298 return V7_TYPE_STRING;
1299 case V7_TAG_BOOLEAN >> 48:
1300 return V7_TYPE_BOOLEAN;
1301 case V7_TAG_FUNCTION >> 48:
1302 return V7_TYPE_FUNCTION_OBJECT;
1303 case V7_TAG_CFUNCTION >> 48:
1304 return V7_TYPE_CFUNCTION;
1305 case V7_TAG_REGEXP >> 48:
1306 return V7_TYPE_REGEXP_OBJECT;
1307 default:
1308 abort();
1309 return V7_TYPE_UNDEFINED;
1310 }
1311}
1312
1313#if !V7_DISABLE_LINE_NUMBERS
1314V7_PRIVATE uint8_t msb_lsb_swap(uint8_t b) {
1315 if ((b & 0x01) != (b >> 7)) {
1316 b ^= 0x81;
1317 }
1318 return b;
1319}
1320#endif
1321#ifdef V7_MODULE_LINES
1322#line 1 "v7/src/string.c"
1323#endif
1324/*
1325 * Copyright (c) 2014 Cesanta Software Limited
1326 * All rights reserved
1327 */
1328
1329/* Amalgamated: #include "common/utf.h" */
1330/* Amalgamated: #include "v7/src/string.h" */
1331/* Amalgamated: #include "v7/src/exceptions.h" */
1332/* Amalgamated: #include "v7/src/conversion.h" */
1333/* Amalgamated: #include "v7/src/varint.h" */
1334/* Amalgamated: #include "v7/src/gc.h" */
1335/* Amalgamated: #include "v7/src/core.h" */
1336/* Amalgamated: #include "v7/src/primitive.h" */
1337/* Amalgamated: #include "v7/src/slre.h" */
1338/* Amalgamated: #include "v7/src/heapusage.h" */
1339
1340/* TODO(lsm): NaN payload location depends on endianness, make crossplatform */
1341#define GET_VAL_NAN_PAYLOAD(v) ((char *) &(v))
1342
1343/*
1344 * Dictionary of read-only strings with length > 5.
1345 * NOTE(lsm): must be sorted lexicographically, because
1346 * v_find_string_in_dictionary performs binary search over this list.
1347 */
1348/* clang-format off */
1349static const struct v7_vec_const v_dictionary_strings[] = {
1350 V7_VEC(" is not a function"),
1351 V7_VEC("Boolean"),
1352 V7_VEC("Crypto"),
1353 V7_VEC("EvalError"),
1354 V7_VEC("Function"),
1355 V7_VEC("Infinity"),
1356 V7_VEC("InternalError"),
1357 V7_VEC("LOG10E"),
1358 V7_VEC("MAX_VALUE"),
1359 V7_VEC("MIN_VALUE"),
1360 V7_VEC("NEGATIVE_INFINITY"),
1361 V7_VEC("Number"),
1362 V7_VEC("Object"),
1363 V7_VEC("POSITIVE_INFINITY"),
1364 V7_VEC("RangeError"),
1365 V7_VEC("ReferenceError"),
1366 V7_VEC("RegExp"),
1367 V7_VEC("SQRT1_2"),
1368 V7_VEC("Socket"),
1369 V7_VEC("String"),
1370 V7_VEC("SyntaxError"),
1371 V7_VEC("TypeError"),
1372 V7_VEC("UBJSON"),
1373 V7_VEC("_modcache"),
1374 V7_VEC("accept"),
1375 V7_VEC("arguments"),
1376 V7_VEC("base64_decode"),
1377 V7_VEC("base64_encode"),
1378 V7_VEC("boolean"),
1379 V7_VEC("charAt"),
1380 V7_VEC("charCodeAt"),
1381 V7_VEC("concat"),
1382 V7_VEC("configurable"),
1383 V7_VEC("connect"),
1384 V7_VEC("constructor"),
1385 V7_VEC("create"),
1386 V7_VEC("defineProperties"),
1387 V7_VEC("defineProperty"),
1388 V7_VEC("every"),
1389 V7_VEC("exists"),
1390 V7_VEC("exports"),
1391 V7_VEC("filter"),
1392 V7_VEC("forEach"),
1393 V7_VEC("fromCharCode"),
1394 V7_VEC("function"),
1395 V7_VEC("getDate"),
1396 V7_VEC("getDay"),
1397 V7_VEC("getFullYear"),
1398 V7_VEC("getHours"),
1399 V7_VEC("getMilliseconds"),
1400 V7_VEC("getMinutes"),
1401 V7_VEC("getMonth"),
1402 V7_VEC("getOwnPropertyDescriptor"),
1403 V7_VEC("getOwnPropertyNames"),
1404 V7_VEC("getPrototypeOf"),
1405 V7_VEC("getSeconds"),
1406 V7_VEC("getTime"),
1407 V7_VEC("getTimezoneOffset"),
1408 V7_VEC("getUTCDate"),
1409 V7_VEC("getUTCDay"),
1410 V7_VEC("getUTCFullYear"),
1411 V7_VEC("getUTCHours"),
1412 V7_VEC("getUTCMilliseconds"),
1413 V7_VEC("getUTCMinutes"),
1414 V7_VEC("getUTCMonth"),
1415 V7_VEC("getUTCSeconds"),
1416 V7_VEC("global"),
1417 V7_VEC("hasOwnProperty"),
1418 V7_VEC("ignoreCase"),
1419 V7_VEC("indexOf"),
1420 V7_VEC("isArray"),
1421 V7_VEC("isExtensible"),
1422 V7_VEC("isFinite"),
1423 V7_VEC("isPrototypeOf"),
1424 V7_VEC("lastIndex"),
1425 V7_VEC("lastIndexOf"),
1426 V7_VEC("length"),
1427 V7_VEC("listen"),
1428 V7_VEC("loadJSON"),
1429 V7_VEC("localeCompare"),
1430 V7_VEC("md5_hex"),
1431 V7_VEC("module"),
1432 V7_VEC("multiline"),
1433 V7_VEC("number"),
1434 V7_VEC("parseFloat"),
1435 V7_VEC("parseInt"),
1436 V7_VEC("preventExtensions"),
1437 V7_VEC("propertyIsEnumerable"),
1438 V7_VEC("prototype"),
1439 V7_VEC("random"),
1440 V7_VEC("recvAll"),
1441 V7_VEC("reduce"),
1442 V7_VEC("remove"),
1443 V7_VEC("rename"),
1444 V7_VEC("render"),
1445 V7_VEC("replace"),
1446 V7_VEC("require"),
1447 V7_VEC("reverse"),
1448 V7_VEC("search"),
1449 V7_VEC("setDate"),
1450 V7_VEC("setFullYear"),
1451 V7_VEC("setHours"),
1452 V7_VEC("setMilliseconds"),
1453 V7_VEC("setMinutes"),
1454 V7_VEC("setMonth"),
1455 V7_VEC("setSeconds"),
1456 V7_VEC("setTime"),
1457 V7_VEC("setUTCDate"),
1458 V7_VEC("setUTCFullYear"),
1459 V7_VEC("setUTCHours"),
1460 V7_VEC("setUTCMilliseconds"),
1461 V7_VEC("setUTCMinutes"),
1462 V7_VEC("setUTCMonth"),
1463 V7_VEC("setUTCSeconds"),
1464 V7_VEC("sha1_hex"),
1465 V7_VEC("source"),
1466 V7_VEC("splice"),
1467 V7_VEC("string"),
1468 V7_VEC("stringify"),
1469 V7_VEC("substr"),
1470 V7_VEC("substring"),
1471 V7_VEC("toDateString"),
1472 V7_VEC("toExponential"),
1473 V7_VEC("toFixed"),
1474 V7_VEC("toISOString"),
1475 V7_VEC("toJSON"),
1476 V7_VEC("toLocaleDateString"),
1477 V7_VEC("toLocaleLowerCase"),
1478 V7_VEC("toLocaleString"),
1479 V7_VEC("toLocaleTimeString"),
1480 V7_VEC("toLocaleUpperCase"),
1481 V7_VEC("toLowerCase"),
1482 V7_VEC("toPrecision"),
1483 V7_VEC("toString"),
1484 V7_VEC("toTimeString"),
1485 V7_VEC("toUTCString"),
1486 V7_VEC("toUpperCase"),
1487 V7_VEC("valueOf"),
1488 V7_VEC("writable"),
1489};
1490/* clang-format on */
1491
1492int nextesc(const char **p); /* from SLRE */
1493V7_PRIVATE size_t unescape(const char *s, size_t len, char *to) {
1494 const char *end = s + len;
1495 size_t n = 0;
1496 char tmp[4];
1497 Rune r;
1498
1499 while (s < end) {
1500 s += chartorune(&r, s);
1501 if (r == '\\' && s < end) {
1502 switch (*s) {
1503 case '"':
1504 s++, r = '"';
1505 break;
1506 case '\'':
1507 s++, r = '\'';
1508 break;
1509 case '\n':
1510 s++, r = '\n';
1511 break;
1512 default: {
1513 const char *tmp_s = s;
1514 int i = nextesc(&s);
1515 switch (i) {
1516 case -SLRE_INVALID_ESC_CHAR:
1517 r = '\\';
1518 s = tmp_s;
1519 n += runetochar(to == NULL ? tmp : to + n, &r);
1520 s += chartorune(&r, s);
1521 break;
1522 case -SLRE_INVALID_HEX_DIGIT:
1523 default:
1524 r = i;
1525 }
1526 }
1527 }
1528 }
1529 n += runetochar(to == NULL ? tmp : to + n, &r);
1530 }
1531
1532 return n;
1533}
1534
1535static int v_find_string_in_dictionary(const char *s, size_t len) {
1536 size_t start = 0, end = ARRAY_SIZE(v_dictionary_strings);
1537
1538 while (s != NULL && start < end) {
1539 size_t mid = start + (end - start) / 2;
1540 const struct v7_vec_const *v = &v_dictionary_strings[mid];
1541 size_t min_len = len < v->len ? len : v->len;
1542 int comparison_result = memcmp(s, v->p, min_len);
1543 if (comparison_result == 0) {
1544 comparison_result = len - v->len;
1545 }
1546 if (comparison_result < 0) {
1547 end = mid;
1548 } else if (comparison_result > 0) {
1549 start = mid + 1;
1550 } else {
1551 return mid;
1552 }
1553 }
1554 return -1;
1555}
1556
1557WARN_UNUSED_RESULT
1558V7_PRIVATE enum v7_err v7_char_code_at(struct v7 *v7, val_t obj, val_t arg,
1559 double *res) {
1560 enum v7_err rcode = V7_OK;
1561 size_t n;
1562 val_t s = V7_UNDEFINED;
1563 const char *p = NULL;
1564 double at = v7_get_double(v7, arg);
1565
1566 *res = 0;
1567
1568 rcode = to_string(v7, obj, &s, NULL, 0, NULL);
1569 if (rcode != V7_OK) {
1570 goto clean;
1571 }
1572
1573 p = v7_get_string(v7, &s, &n);
1574
1575 n = utfnlen(p, n);
1576 if (v7_is_number(arg) && at >= 0 && at < n) {
1577 Rune r = 0;
1578 p = utfnshift(p, at);
1579 chartorune(&r, (char *) p);
1580 *res = r;
1581 goto clean;
1582 } else {
1583 *res = NAN;
1584 goto clean;
1585 }
1586
1587clean:
1588 return rcode;
1589}
1590
1591V7_PRIVATE int s_cmp(struct v7 *v7, val_t a, val_t b) {
1592 size_t a_len, b_len;
1593 const char *a_ptr, *b_ptr;
1594
1595 a_ptr = v7_get_string(v7, &a, &a_len);
1596 b_ptr = v7_get_string(v7, &b, &b_len);
1597
1598 if (a_len == b_len) {
1599 return memcmp(a_ptr, b_ptr, a_len);
1600 }
1601 if (a_len > b_len) {
1602 return 1;
1603 } else if (a_len < b_len) {
1604 return -1;
1605 } else {
1606 return 0;
1607 }
1608}
1609
1610V7_PRIVATE val_t s_concat(struct v7 *v7, val_t a, val_t b) {
1611 size_t a_len, b_len, res_len;
1612 const char *a_ptr, *b_ptr, *res_ptr;
1613 val_t res;
1614
1615 /* Find out lengths of both srtings */
1616 a_ptr = v7_get_string(v7, &a, &a_len);
1617 b_ptr = v7_get_string(v7, &b, &b_len);
1618
1619 /* Create an placeholder string */
1620 res = v7_mk_string(v7, NULL, a_len + b_len, 1);
1621
1622 /* v7_mk_string() may have reallocated mbuf - revalidate pointers */
1623 a_ptr = v7_get_string(v7, &a, &a_len);
1624 b_ptr = v7_get_string(v7, &b, &b_len);
1625
1626 /* Copy strings into the placeholder */
1627 res_ptr = v7_get_string(v7, &res, &res_len);
1628 memcpy((char *) res_ptr, a_ptr, a_len);
1629 memcpy((char *) res_ptr + a_len, b_ptr, b_len);
1630
1631 return res;
1632}
1633
1634V7_PRIVATE unsigned long cstr_to_ulong(const char *s, size_t len, int *ok) {
1635 char *e;
1636 unsigned long res = strtoul(s, &e, 10);
1637 *ok = (e == s + len) && len != 0;
1638 return res;
1639}
1640
1641WARN_UNUSED_RESULT
1642V7_PRIVATE enum v7_err str_to_ulong(struct v7 *v7, val_t v, int *ok,
1643 unsigned long *res) {
1644 enum v7_err rcode = V7_OK;
1645 char buf[100];
1646 size_t len = 0;
1647
1648 V7_TRY(to_string(v7, v, NULL, buf, sizeof(buf), &len));
1649
1650 *res = cstr_to_ulong(buf, len, ok);
1651
1652clean:
1653 return rcode;
1654}
1655
1656/* Insert a string into mbuf at specified offset */
1657V7_PRIVATE void embed_string(struct mbuf *m, size_t offset, const char *p,
1658 size_t len, uint8_t /*enum embstr_flags*/ flags) {
1659 char *old_base = m->buf;
1660 uint8_t p_backed_by_mbuf = p >= old_base && p < old_base + m->len;
1661 size_t n = (flags & EMBSTR_UNESCAPE) ? unescape(p, len, NULL) : len;
1662
1663 /* Calculate how many bytes length takes */
1664 int k = calc_llen(n);
1665
1666 /* total length: varing length + string len + zero-term */
1667 size_t tot_len = k + n + !!(flags & EMBSTR_ZERO_TERM);
1668
1669 /* Allocate buffer */
1670 heapusage_dont_count(1);
1671 mbuf_insert(m, offset, NULL, tot_len);
1672 heapusage_dont_count(0);
1673
1674 /* Fixup p if it was relocated by mbuf_insert() above */
1675 if (p_backed_by_mbuf) {
1676 p += m->buf - old_base;
1677 }
1678
1679 /* Write length */
1680 encode_varint(n, (unsigned char *) m->buf + offset);
1681
1682 /* Write string */
1683 if (p != 0) {
1684 if (flags & EMBSTR_UNESCAPE) {
1685 unescape(p, len, m->buf + offset + k);
1686 } else {
1687 memcpy(m->buf + offset + k, p, len);
1688 }
1689 }
1690
1691 /* add NULL-terminator if needed */
1692 if (flags & EMBSTR_ZERO_TERM) {
1693 m->buf[offset + tot_len - 1] = '\0';
1694 }
1695}
1696
1697/* Create a string */
1698v7_val_t v7_mk_string(struct v7 *v7, const char *p, size_t len, int copy) {
1699 struct mbuf *m = copy ? &v7->owned_strings : &v7->foreign_strings;
1700 val_t offset = m->len, tag = V7_TAG_STRING_F;
1701 int dict_index;
1702
1703#ifdef V7_GC_AFTER_STRING_ALLOC
1704 v7->need_gc = 1;
1705#endif
1706
1707 if (len == ~((size_t) 0)) len = strlen(p);
1708
1709 if (len <= 4) {
1710 char *s = GET_VAL_NAN_PAYLOAD(offset) + 1;
1711 offset = 0;
1712 if (p != 0) {
1713 memcpy(s, p, len);
1714 }
1715 s[-1] = len;
1716 tag = V7_TAG_STRING_I;
1717 } else if (len == 5) {
1718 char *s = GET_VAL_NAN_PAYLOAD(offset);
1719 offset = 0;
1720 if (p != 0) {
1721 memcpy(s, p, len);
1722 }
1723 tag = V7_TAG_STRING_5;
1724 } else if ((dict_index = v_find_string_in_dictionary(p, len)) >= 0) {
1725 offset = 0;
1726 GET_VAL_NAN_PAYLOAD(offset)[0] = dict_index;
1727 tag = V7_TAG_STRING_D;
1728 } else if (copy) {
1729 compute_need_gc(v7);
1730
1731 /*
1732 * Before embedding new string, check if the reallocation is needed. If
1733 * so, perform the reallocation by calling `mbuf_resize` manually, since we
1734 * need to preallocate some extra space (`_V7_STRING_BUF_RESERVE`)
1735 */
1736 if ((m->len + len) > m->size) {
1737 heapusage_dont_count(1);
1738 mbuf_resize(m, m->len + len + _V7_STRING_BUF_RESERVE);
1739 heapusage_dont_count(0);
1740 }
1741 embed_string(m, m->len, p, len, EMBSTR_ZERO_TERM);
1742 tag = V7_TAG_STRING_O;
1743#if !V7_DISABLE_STR_ALLOC_SEQ
1744 /* TODO(imax): panic if offset >= 2^32. */
1745 offset |= ((val_t) gc_next_allocation_seqn(v7, p, len)) << 32;
1746#endif
1747 } else {
1748 /* foreign string */
1749 if (sizeof(void *) <= 4 && len <= UINT16_MAX) {
1750 /* small foreign strings can fit length and ptr in the val_t */
1751 offset = (uint64_t) len << 32 | (uint64_t)(uintptr_t) p;
1752 } else {
1753 /* bigger strings need indirection that uses ram */
1754 size_t pos = m->len;
1755 int llen = calc_llen(len);
1756
1757 /* allocate space for len and ptr */
1758 heapusage_dont_count(1);
1759 mbuf_insert(m, pos, NULL, llen + sizeof(p));
1760 heapusage_dont_count(0);
1761
1762 encode_varint(len, (uint8_t *) (m->buf + pos));
1763 memcpy(m->buf + pos + llen, &p, sizeof(p));
1764 }
1765 tag = V7_TAG_STRING_F;
1766 }
1767
1768 /* NOTE(lsm): don't use pointer_to_value, 32-bit ptrs will truncate */
1769 return (offset & ~V7_TAG_MASK) | tag;
1770}
1771
1772int v7_is_string(val_t v) {
1773 uint64_t t = v & V7_TAG_MASK;
1774 return t == V7_TAG_STRING_I || t == V7_TAG_STRING_F || t == V7_TAG_STRING_O ||
1775 t == V7_TAG_STRING_5 || t == V7_TAG_STRING_D;
1776}
1777
1778/* Get a pointer to string and string length. */
1779const char *v7_get_string(struct v7 *v7, val_t *v, size_t *sizep) {
1780 uint64_t tag = v[0] & V7_TAG_MASK;
1781 const char *p = NULL;
1782 int llen;
1783 size_t size = 0;
1784
1785 if (!v7_is_string(*v)) {
1786 goto clean;
1787 }
1788
1789 if (tag == V7_TAG_STRING_I) {
1790 p = GET_VAL_NAN_PAYLOAD(*v) + 1;
1791 size = p[-1];
1792 } else if (tag == V7_TAG_STRING_5) {
1793 p = GET_VAL_NAN_PAYLOAD(*v);
1794 size = 5;
1795 } else if (tag == V7_TAG_STRING_D) {
1796 int index = ((unsigned char *) GET_VAL_NAN_PAYLOAD(*v))[0];
1797 size = v_dictionary_strings[index].len;
1798 p = v_dictionary_strings[index].p;
1799 } else if (tag == V7_TAG_STRING_O) {
1800 size_t offset = (size_t) gc_string_val_to_offset(*v);
1801 char *s = v7->owned_strings.buf + offset;
1802
1803#if !V7_DISABLE_STR_ALLOC_SEQ
1804 gc_check_valid_allocation_seqn(v7, (*v >> 32) & 0xFFFF);
1805#endif
1806
1807 size = decode_varint((uint8_t *) s, &llen);
1808 p = s + llen;
1809 } else if (tag == V7_TAG_STRING_F) {
1810 /*
1811 * short foreign strings on <=32-bit machines can be encoded in a compact
1812 * form:
1813 *
1814 * 7 6 5 4 3 2 1 0
1815 * 11111111|1111tttt|llllllll|llllllll|ssssssss|ssssssss|ssssssss|ssssssss
1816 *
1817 * Strings longer than 2^26 will be indireceted through the foreign_strings
1818 * mbuf.
1819 *
1820 * We don't use a different tag to represent those two cases. Instead, all
1821 * foreign strings represented with the help of the foreign_strings mbuf
1822 * will have the upper 16-bits of the payload set to zero. This allows us to
1823 * represent up to 477 million foreign strings longer than 64k.
1824 */
1825 uint16_t len = (*v >> 32) & 0xFFFF;
1826 if (sizeof(void *) <= 4 && len != 0) {
1827 size = (size_t) len;
1828 p = (const char *) (uintptr_t) *v;
1829 } else {
1830 size_t offset = (size_t) gc_string_val_to_offset(*v);
1831 char *s = v7->foreign_strings.buf + offset;
1832
1833 size = decode_varint((uint8_t *) s, &llen);
1834 memcpy(&p, s + llen, sizeof(p));
1835 }
1836 } else {
1837 assert(0);
1838 }
1839
1840clean:
1841 if (sizep != NULL) {
1842 *sizep = size;
1843 }
1844 return p;
1845}
1846
1847const char *v7_get_cstring(struct v7 *v7, v7_val_t *value) {
1848 size_t size;
1849 const char *s = v7_get_string(v7, value, &size);
1850 if (s == NULL) return NULL;
1851 if (s[size] != 0 || strlen(s) != size) {
1852 return NULL;
1853 }
1854 return s;
1855}
1856#ifdef V7_MODULE_LINES
1857#line 1 "v7/src/array.c"
1858#endif
1859/*
1860 * Copyright (c) 2014 Cesanta Software Limited
1861 * All rights reserved
1862 */
1863
1864/* Amalgamated: #include "common/str_util.h" */
1865/* Amalgamated: #include "v7/src/internal.h" */
1866/* Amalgamated: #include "v7/src/array.h" */
1867/* Amalgamated: #include "v7/src/string.h" */
1868/* Amalgamated: #include "v7/src/object.h" */
1869/* Amalgamated: #include "v7/src/exceptions.h" */
1870/* Amalgamated: #include "v7/src/primitive.h" */
1871/* Amalgamated: #include "v7/src/core.h" */
1872
1873/* like c_snprintf but returns `size` if write is truncated */
1874static int v_sprintf_s(char *buf, size_t size, const char *fmt, ...) {
1875 size_t n;
1876 va_list ap;
1877 va_start(ap, fmt);
1878 n = c_vsnprintf(buf, size, fmt, ap);
1879 if (n > size) {
1880 return size;
1881 }
1882 return n;
1883}
1884
1885v7_val_t v7_mk_array(struct v7 *v7) {
1886 val_t a = mk_object(v7, v7->vals.array_prototype);
1887#if 0
1888 v7_def(v7, a, "", 0, _V7_DESC_HIDDEN(1), V7_NULL);
1889#endif
1890 return a;
1891}
1892
1893int v7_is_array(struct v7 *v7, val_t v) {
1894 return v7_is_generic_object(v) &&
1895 is_prototype_of(v7, v, v7->vals.array_prototype);
1896}
1897
1898/*
1899 * Dense arrays are backed by mbuf. Currently the array can only grow by
1900 * appending (i.e. setting an element whose index == array.length)
1901 *
1902 * TODO(mkm): automatically promote dense arrays to normal objects
1903 * when they are used as sparse arrays or to store arbitrary keys
1904 * (perhaps a hybrid approach)
1905 * TODO(mkm): small sparsness doesn't have to promote the array,
1906 * we can just fill empty slots with a tag. In JS missing array
1907 * indices are subtly different from indices with an undefined value
1908 * (key iteration).
1909 * TODO(mkm): change the interpreter so it can set elements in dense arrays
1910 */
1911V7_PRIVATE val_t v7_mk_dense_array(struct v7 *v7) {
1912 val_t a = v7_mk_array(v7);
1913#if V7_ENABLE_DENSE_ARRAYS
1914 v7_own(v7, &a);
1915 v7_def(v7, a, "", 0, _V7_DESC_HIDDEN(1), V7_NULL);
1916
1917 /*
1918 * Before setting a `V7_OBJ_DENSE_ARRAY` flag, make sure we don't have
1919 * `V7_OBJ_FUNCTION` flag set
1920 */
1921 assert(!(get_object_struct(a)->attributes & V7_OBJ_FUNCTION));
1922 get_object_struct(a)->attributes |= V7_OBJ_DENSE_ARRAY;
1923
1924 v7_disown(v7, &a);
1925#endif
1926 return a;
1927}
1928
1929/* TODO_V7_ERR */
1930val_t v7_array_get(struct v7 *v7, val_t arr, unsigned long index) {
1931 return v7_array_get2(v7, arr, index, NULL);
1932}
1933
1934/* TODO_V7_ERR */
1935val_t v7_array_get2(struct v7 *v7, val_t arr, unsigned long index, int *has) {
1936 enum v7_err rcode = V7_OK;
1937 val_t res;
1938
1939 if (has != NULL) {
1940 *has = 0;
1941 }
1942 if (v7_is_object(arr)) {
1943 if (get_object_struct(arr)->attributes & V7_OBJ_DENSE_ARRAY) {
1944 struct v7_property *p =
1945 v7_get_own_property2(v7, arr, "", 0, _V7_PROPERTY_HIDDEN);
1946 struct mbuf *abuf = NULL;
1947 unsigned long len;
1948 if (p != NULL) {
1949 abuf = (struct mbuf *) v7_get_ptr(v7, p->value);
1950 }
1951 if (abuf == NULL) {
1952 res = V7_UNDEFINED;
1953 goto clean;
1954 }
1955 len = abuf->len / sizeof(val_t);
1956 if (index >= len) {
1957 res = V7_UNDEFINED;
1958 goto clean;
1959 } else {
1960 memcpy(&res, abuf->buf + index * sizeof(val_t), sizeof(val_t));
1961 if (has != NULL && res != V7_TAG_NOVALUE) *has = 1;
1962 if (res == V7_TAG_NOVALUE) {
1963 res = V7_UNDEFINED;
1964 }
1965 goto clean;
1966 }
1967 } else {
1968 struct v7_property *p;
1969 char buf[20];
1970 int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
1971 p = v7_get_property(v7, arr, buf, n);
1972 if (has != NULL && p != NULL) *has = 1;
1973 V7_TRY(v7_property_value(v7, arr, p, &res));
1974 goto clean;
1975 }
1976 } else {
1977 res = V7_UNDEFINED;
1978 goto clean;
1979 }
1980
1981clean:
1982 (void) rcode;
1983 return res;
1984}
1985
1986#if V7_ENABLE_DENSE_ARRAYS
1987
1988/* Create V7 strings for integers such as array indices */
1989static val_t ulong_to_str(struct v7 *v7, unsigned long n) {
1990 char buf[100];
1991 int len;
1992 len = c_snprintf(buf, sizeof(buf), "%lu", n);
1993 return v7_mk_string(v7, buf, len, 1);
1994}
1995
1996/*
1997 * Pack 15-bit length and 15 bit index, leaving 2 bits for tag. the LSB has to
1998 * be set to distinguish it from a prop pointer.
1999 * In alternative we just fetch the length from obj at each call to v7_next_prop
2000 * and just stuff the index here (e.g. on 8/16-bit platforms).
2001 * TODO(mkm): conditional for 16-bit platforms
2002 */
2003#define PACK_ITER(len, idx) \
2004 ((struct v7_property *) ((len) << 17 | (idx) << 1 | 1))
2005
2006#define UNPACK_ITER_LEN(p) (((uintptr_t) p) >> 17)
2007#define UNPACK_ITER_IDX(p) ((((uintptr_t) p) >> 1) & 0x7FFF)
2008#define IS_PACKED_ITER(p) ((uintptr_t) p & 1)
2009
2010void *v7_next_prop(struct v7 *v7, val_t obj, void *h, val_t *name, val_t *val,
2011 v7_prop_attr_t *attrs) {
2012 struct v7_property *p = (struct v7_property *) h;
2013
2014 if (get_object_struct(obj)->attributes & V7_OBJ_DENSE_ARRAY) {
2015 /* This is a dense array. Find backing mbuf and fetch values from there */
2016 struct v7_property *hp =
2017 v7_get_own_property2(v7, obj, "", 0, _V7_PROPERTY_HIDDEN);
2018 struct mbuf *abuf = NULL;
2019 unsigned long len, idx;
2020 if (hp != NULL) {
2021 abuf = (struct mbuf *) v7_get_ptr(v7, hp->value);
2022 }
2023 if (abuf == NULL) return NULL;
2024 len = abuf->len / sizeof(val_t);
2025 if (len == 0) return NULL;
2026 idx = (p == NULL) ? 0 : UNPACK_ITER_IDX(p) + 1;
2027 p = (idx == len) ? get_object_struct(obj)->properties : PACK_ITER(len, idx);
2028 if (val != NULL) *val = ((val_t *) abuf->buf)[idx];
2029 if (attrs != NULL) *attrs = 0;
2030 if (name != NULL) {
2031 char buf[20];
2032 int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
2033 *name = v7_mk_string(v7, buf, n, 1);
2034 }
2035 } else {
2036 /* Ordinary object */
2037 p = (p == NULL) ? get_object_struct(obj)->properties : p->next;
2038 if (p != NULL) {
2039 if (name != NULL) *name = p->name;
2040 if (val != NULL) *val = p->value;
2041 if (attrs != NULL) *attrs = p->attributes;
2042 }
2043 }
2044
2045 return p;
2046}
2047
2048V7_PRIVATE val_t
2049v7_iter_get_value(struct v7 *v7, val_t obj, struct v7_property *p) {
2050 return IS_PACKED_ITER(p) ? v7_array_get(v7, obj, UNPACK_ITER_IDX(p))
2051 : p->value;
2052}
2053
2054V7_PRIVATE val_t v7_iter_get_name(struct v7 *v7, struct v7_property *p) {
2055 return IS_PACKED_ITER(p) ? ulong_to_str(v7, UNPACK_ITER_IDX(p)) : p->name;
2056}
2057
2058V7_PRIVATE uint8_t v7_iter_get_attrs(struct v7_property *p) {
2059 return IS_PACKED_ITER(p) ? 0 : p->attributes;
2060}
2061
2062/* return array index as number or undefined. works with iterators */
2063V7_PRIVATE enum v7_err v7_iter_get_index(struct v7 *v7, struct v7_property *p,
2064 val_t *res) {
2065 enum v7_err rcode = V7_OK;
2066 int ok;
2067 unsigned long res;
2068 if (IS_PACKED_ITER(p)) {
2069 *res = v7_mk_number(v7, UNPACK_ITER_IDX(p));
2070 goto clean;
2071 }
2072 V7_TRY(str_to_ulong(v7, p->name, &ok, &res));
2073 if (!ok || res >= UINT32_MAX) {
2074 goto clean;
2075 }
2076 *res = v7_mk_number(v7, res);
2077 goto clean;
2078
2079clean:
2080 return rcode;
2081}
2082#endif
2083
2084/* TODO_V7_ERR */
2085unsigned long v7_array_length(struct v7 *v7, val_t v) {
2086 enum v7_err rcode = V7_OK;
2087 struct v7_property *p;
2088 unsigned long len = 0;
2089
2090 if (!v7_is_object(v)) {
2091 len = 0;
2092 goto clean;
2093 }
2094
2095#if V7_ENABLE_DENSE_ARRAYS
2096 if (get_object_struct(v)->attributes & V7_OBJ_DENSE_ARRAY) {
2097 struct v7_property *p =
2098 v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
2099 struct mbuf *abuf;
2100 if (p == NULL) {
2101 len = 0;
2102 goto clean;
2103 }
2104 abuf = (struct mbuf *) v7_get_ptr(v7, p->value);
2105 if (abuf == NULL) {
2106 len = 0;
2107 goto clean;
2108 }
2109 len = abuf->len / sizeof(val_t);
2110 goto clean;
2111 }
2112#endif
2113
2114 for (p = get_object_struct(v)->properties; p != NULL; p = p->next) {
2115 int ok = 0;
2116 unsigned long n = 0;
2117 V7_TRY(str_to_ulong(v7, p->name, &ok, &n));
2118 if (ok && n >= len && n < UINT32_MAX) {
2119 len = n + 1;
2120 }
2121 }
2122
2123clean:
2124 (void) rcode;
2125 return len;
2126}
2127
2128int v7_array_set(struct v7 *v7, val_t arr, unsigned long index, val_t v) {
2129 enum v7_err rcode = V7_OK;
2130 uint8_t saved_is_thrown = 0;
2131 val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
2132 int ret = -1;
2133
2134 rcode = v7_array_set_throwing(v7, arr, index, v, &ret);
2135 if (rcode != V7_OK) {
2136 rcode = V7_OK;
2137 if (saved_is_thrown) {
2138 rcode = v7_throw(v7, saved_thrown);
2139 } else {
2140 v7_clear_thrown_value(v7);
2141 }
2142 ret = -1;
2143 }
2144
2145 return ret;
2146}
2147
2148enum v7_err v7_array_set_throwing(struct v7 *v7, val_t arr, unsigned long index,
2149 val_t v, int *res) {
2150 enum v7_err rcode = V7_OK;
2151 int ires = -1;
2152
2153 if (v7_is_object(arr)) {
2154 if (get_object_struct(arr)->attributes & V7_OBJ_DENSE_ARRAY) {
2155 struct v7_property *p =
2156 v7_get_own_property2(v7, arr, "", 0, _V7_PROPERTY_HIDDEN);
2157 struct mbuf *abuf;
2158 unsigned long len;
2159 assert(p != NULL);
2160 abuf = (struct mbuf *) v7_get_ptr(v7, p->value);
2161
2162 if (get_object_struct(arr)->attributes & V7_OBJ_NOT_EXTENSIBLE) {
2163 if (is_strict_mode(v7)) {
2164 rcode = v7_throwf(v7, TYPE_ERROR, "Object is not extensible");
2165 goto clean;
2166 }
2167
2168 goto clean;
2169 }
2170
2171 if (abuf == NULL) {
2172 abuf = (struct mbuf *) malloc(sizeof(*abuf));
2173 mbuf_init(abuf, sizeof(val_t) * (index + 1));
2174 p->value = v7_mk_foreign(v7, abuf);
2175 }
2176 len = abuf->len / sizeof(val_t);
2177 /* TODO(mkm): possibly promote to sparse array */
2178 if (index > len) {
2179 unsigned long i;
2180 val_t s = V7_TAG_NOVALUE;
2181 for (i = len; i < index; i++) {
2182 mbuf_append(abuf, (char *) &s, sizeof(val_t));
2183 }
2184 len = index;
2185 }
2186
2187 if (index == len) {
2188 mbuf_append(abuf, (char *) &v, sizeof(val_t));
2189 } else {
2190 memcpy(abuf->buf + index * sizeof(val_t), &v, sizeof(val_t));
2191 }
2192 } else {
2193 char buf[20];
2194 int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
2195 {
2196 struct v7_property *tmp = NULL;
2197 rcode = set_property(v7, arr, buf, n, v, &tmp);
2198 ires = (tmp == NULL) ? -1 : 0;
2199 }
2200 if (rcode != V7_OK) {
2201 goto clean;
2202 }
2203 }
2204 }
2205
2206clean:
2207 if (res != NULL) {
2208 *res = ires;
2209 }
2210 return rcode;
2211}
2212
2213void v7_array_del(struct v7 *v7, val_t arr, unsigned long index) {
2214 char buf[20];
2215 int n = v_sprintf_s(buf, sizeof(buf), "%lu", index);
2216 v7_del(v7, arr, buf, n);
2217}
2218
2219int v7_array_push(struct v7 *v7, v7_val_t arr, v7_val_t v) {
2220 return v7_array_set(v7, arr, v7_array_length(v7, arr), v);
2221}
2222
2223WARN_UNUSED_RESULT
2224enum v7_err v7_array_push_throwing(struct v7 *v7, v7_val_t arr, v7_val_t v,
2225 int *res) {
2226 return v7_array_set_throwing(v7, arr, v7_array_length(v7, arr), v, res);
2227}
2228#ifdef V7_MODULE_LINES
2229#line 1 "v7/src/object.c"
2230#endif
2231/*
2232 * Copyright (c) 2014 Cesanta Software Limited
2233 * All rights reserved
2234 */
2235
2236/* Amalgamated: #include "v7/src/internal.h" */
2237/* Amalgamated: #include "v7/src/core.h" */
2238/* Amalgamated: #include "v7/src/primitive.h" */
2239/* Amalgamated: #include "v7/src/function.h" */
2240/* Amalgamated: #include "v7/src/gc.h" */
2241/* Amalgamated: #include "v7/src/object.h" */
2242/* Amalgamated: #include "v7/src/string.h" */
2243/* Amalgamated: #include "v7/src/array.h" */
2244/* Amalgamated: #include "v7/src/eval.h" */
2245/* Amalgamated: #include "v7/src/exceptions.h" */
2246/* Amalgamated: #include "v7/src/conversion.h" */
2247/* Amalgamated: #include "v7/src/std_proxy.h" */
2248/* Amalgamated: #include "v7/src/util.h" */
2249
2250/*
2251 * Default property attributes (see `v7_prop_attr_t`)
2252 */
2253#define V7_DEFAULT_PROPERTY_ATTRS 0
2254
2255V7_PRIVATE val_t mk_object(struct v7 *v7, val_t prototype) {
2256 struct v7_generic_object *o = new_generic_object(v7);
2257 if (o == NULL) {
2258 return V7_NULL;
2259 }
2260 (void) v7;
2261#if V7_ENABLE_ENTITY_IDS
2262 o->base.entity_id_base = V7_ENTITY_ID_PART_OBJ;
2263 o->base.entity_id_spec = V7_ENTITY_ID_PART_GEN_OBJ;
2264#endif
2265 o->base.properties = NULL;
2266 obj_prototype_set(v7, &o->base, get_object_struct(prototype));
2267 return v7_object_to_value(&o->base);
2268}
2269
2270v7_val_t v7_mk_object(struct v7 *v7) {
2271 return mk_object(v7, v7->vals.object_prototype);
2272}
2273
2274V7_PRIVATE val_t v7_object_to_value(struct v7_object *o) {
2275 if (o == NULL) {
2276 return V7_NULL;
2277 } else if (o->attributes & V7_OBJ_FUNCTION) {
2278 return pointer_to_value(o) | V7_TAG_FUNCTION;
2279 } else {
2280 return pointer_to_value(o) | V7_TAG_OBJECT;
2281 }
2282}
2283
2284V7_PRIVATE struct v7_generic_object *get_generic_object_struct(val_t v) {
2285 struct v7_generic_object *ret = NULL;
2286 if (v7_is_null(v)) {
2287 ret = NULL;
2288 } else {
2289 assert(v7_is_generic_object(v));
2290 ret = (struct v7_generic_object *) get_ptr(v);
2291#if V7_ENABLE_ENTITY_IDS
2292 if (ret->base.entity_id_base != V7_ENTITY_ID_PART_OBJ) {
2293 fprintf(stderr, "not a generic object!\n");
2294 abort();
2295 } else if (ret->base.entity_id_spec != V7_ENTITY_ID_PART_GEN_OBJ) {
2296 fprintf(stderr, "not an object (but is a generic object)!\n");
2297 abort();
2298 }
2299#endif
2300 }
2301 return ret;
2302}
2303
2304V7_PRIVATE struct v7_object *get_object_struct(val_t v) {
2305 struct v7_object *ret = NULL;
2306 if (v7_is_null(v)) {
2307 ret = NULL;
2308 } else {
2309 assert(v7_is_object(v));
2310 ret = (struct v7_object *) get_ptr(v);
2311#if V7_ENABLE_ENTITY_IDS
2312 if (ret->entity_id_base != V7_ENTITY_ID_PART_OBJ) {
2313 fprintf(stderr, "not an object!\n");
2314 abort();
2315 }
2316#endif
2317 }
2318 return ret;
2319}
2320
2321int v7_is_object(val_t v) {
2322 return (v & V7_TAG_MASK) == V7_TAG_OBJECT ||
2323 (v & V7_TAG_MASK) == V7_TAG_FUNCTION;
2324}
2325
2326V7_PRIVATE int v7_is_generic_object(val_t v) {
2327 return (v & V7_TAG_MASK) == V7_TAG_OBJECT;
2328}
2329
2330/* Object properties {{{ */
2331
2332V7_PRIVATE struct v7_property *v7_mk_property(struct v7 *v7) {
2333 struct v7_property *p = new_property(v7);
2334#if V7_ENABLE_ENTITY_IDS
2335 p->entity_id = V7_ENTITY_ID_PROP;
2336#endif
2337 p->next = NULL;
2338 p->name = V7_UNDEFINED;
2339 p->value = V7_UNDEFINED;
2340 p->attributes = 0;
2341 return p;
2342}
2343
2344V7_PRIVATE struct v7_property *v7_get_own_property2(struct v7 *v7, val_t obj,
2345 const char *name,
2346 size_t len,
2347 v7_prop_attr_t attrs) {
2348 struct v7_property *p;
2349 struct v7_object *o;
2350 val_t ss;
2351 if (!v7_is_object(obj)) {
2352 return NULL;
2353 }
2354 if (len == (size_t) ~0) {
2355 len = strlen(name);
2356 }
2357
2358 o = get_object_struct(obj);
2359 /*
2360 * len check is needed to allow getting the mbuf from the hidden property.
2361 * TODO(mkm): however hidden properties cannot be safely represented with
2362 * a zero length string anyway, so this will change.
2363 */
2364 if (o->attributes & V7_OBJ_DENSE_ARRAY && len > 0) {
2365 int ok, has;
2366 unsigned long i = cstr_to_ulong(name, len, &ok);
2367 if (ok) {
2368 v7->cur_dense_prop->value = v7_array_get2(v7, obj, i, &has);
2369 return has ? v7->cur_dense_prop : NULL;
2370 }
2371 }
2372
2373 if (len <= 5) {
2374 ss = v7_mk_string(v7, name, len, 1);
2375 for (p = o->properties; p != NULL; p = p->next) {
2376#if V7_ENABLE_ENTITY_IDS
2377 if (p->entity_id != V7_ENTITY_ID_PROP) {
2378 fprintf(stderr, "not a prop!=0x%x\n", p->entity_id);
2379 abort();
2380 }
2381#endif
2382 if (p->name == ss && (attrs == 0 || (p->attributes & attrs))) {
2383 return p;
2384 }
2385 }
2386 } else {
2387 for (p = o->properties; p != NULL; p = p->next) {
2388 size_t n;
2389 const char *s = v7_get_string(v7, &p->name, &n);
2390#if V7_ENABLE_ENTITY_IDS
2391 if (p->entity_id != V7_ENTITY_ID_PROP) {
2392 fprintf(stderr, "not a prop!=0x%x\n", p->entity_id);
2393 abort();
2394 }
2395#endif
2396 if (n == len && strncmp(s, name, len) == 0 &&
2397 (attrs == 0 || (p->attributes & attrs))) {
2398 return p;
2399 }
2400 }
2401 }
2402 return NULL;
2403}
2404
2405V7_PRIVATE struct v7_property *v7_get_own_property(struct v7 *v7, val_t obj,
2406 const char *name,
2407 size_t len) {
2408 return v7_get_own_property2(v7, obj, name, len, 0);
2409}
2410
2411V7_PRIVATE struct v7_property *v7_get_property(struct v7 *v7, val_t obj,
2412 const char *name, size_t len) {
2413 if (!v7_is_object(obj)) {
2414 return NULL;
2415 }
2416 for (; obj != V7_NULL; obj = v7_get_proto(v7, obj)) {
2417 struct v7_property *prop;
2418 if ((prop = v7_get_own_property(v7, obj, name, len)) != NULL) {
2419 return prop;
2420 }
2421 }
2422 return NULL;
2423}
2424
2425V7_PRIVATE enum v7_err v7_get_property_v(struct v7 *v7, val_t obj,
2426 v7_val_t name,
2427 struct v7_property **res) {
2428 enum v7_err rcode = V7_OK;
2429 size_t name_len;
2430 STATIC char buf[8];
2431 const char *s = buf;
2432 uint8_t fr = 0;
2433
2434 if (v7_is_string(name)) {
2435 s = v7_get_string(v7, &name, &name_len);
2436 } else {
2437 char *stmp;
2438 V7_TRY(v7_stringify_throwing(v7, name, buf, sizeof(buf),
2439 V7_STRINGIFY_DEFAULT, &stmp));
2440 s = stmp;
2441 if (s != buf) {
2442 fr = 1;
2443 }
2444 name_len = strlen(s);
2445 }
2446
2447 *res = v7_get_property(v7, obj, s, name_len);
2448
2449clean:
2450 if (fr) {
2451 free((void *) s);
2452 }
2453 return rcode;
2454}
2455
2456WARN_UNUSED_RESULT
2457enum v7_err v7_get_throwing(struct v7 *v7, val_t obj, const char *name,
2458 size_t name_len, val_t *res) {
2459 enum v7_err rcode = V7_OK;
2460 val_t v = obj;
2461
2462 v7_own(v7, &v);
2463
2464 if (name_len == (size_t) ~0) {
2465 name_len = strlen(name);
2466 }
2467
2468 if (v7_is_string(obj)) {
2469 v = v7->vals.string_prototype;
2470 } else if (v7_is_number(obj)) {
2471 v = v7->vals.number_prototype;
2472 } else if (v7_is_boolean(obj)) {
2473 v = v7->vals.boolean_prototype;
2474 } else if (v7_is_undefined(obj)) {
2475 rcode =
2476 v7_throwf(v7, TYPE_ERROR, "cannot read property '%.*s' of undefined",
2477 (int) name_len, name);
2478 goto clean;
2479 } else if (v7_is_null(obj)) {
2480 rcode = v7_throwf(v7, TYPE_ERROR, "cannot read property '%.*s' of null",
2481 (int) name_len, name);
2482 goto clean;
2483 } else if (is_cfunction_lite(obj)) {
2484 v = v7->vals.function_prototype;
2485 }
2486
2487#if V7_ENABLE__Proxy
2488 {
2489 struct v7_object *o = NULL;
2490 if (v7_is_object(obj)) {
2491 o = get_object_struct(obj);
2492 }
2493
2494 if (o != NULL && (o->attributes & V7_OBJ_PROXY) &&
2495 !is_special_proxy_name(name, name_len)) {
2496 /* we need to access the target object through a proxy */
2497
2498 val_t target_v = V7_UNDEFINED;
2499 val_t handler_v = V7_UNDEFINED;
2500 val_t name_v = V7_UNDEFINED;
2501 val_t get_v = V7_UNDEFINED;
2502 val_t get_args_v = V7_UNDEFINED;
2503
2504 /*
2505 * we need to create a copy of the name, because the given `name` might
2506 * be returned by v7_get_string(), and any object creation might
2507 * invalidate this pointer. Below, we're going to create some objects.
2508 *
2509 * It would probably be cleaner to always create a copy before calling
2510 * v7_get_throwing if the name was returned by v7_get_string(), but that
2511 * would cause additional pressure on the heap, so let's not do that
2512 */
2513 char *name_copy = (char *) calloc(1, name_len + 1 /* null-term */);
2514 memcpy(name_copy, name, name_len);
2515
2516 v7_own(v7, &target_v);
2517 v7_own(v7, &handler_v);
2518 v7_own(v7, &name_v);
2519 v7_own(v7, &get_v);
2520 v7_own(v7, &get_args_v);
2521
2522 V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_TARGET_NAME, ~0, &target_v),
2523 clean_proxy);
2524 V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_HANDLER_NAME, ~0, &handler_v),
2525 clean_proxy);
2526 V7_TRY2(v7_get_throwing(v7, handler_v, "get", ~0, &get_v), clean_proxy);
2527
2528 if (v7_is_callable(v7, get_v)) {
2529 /* The `get` callback is actually callable, so, use it */
2530
2531 /* prepare arguments for the callback */
2532 get_args_v = v7_mk_dense_array(v7);
2533 /*
2534 * TODO(dfrank): don't copy string in case we already have val_t (we
2535 * need some generic function which will take both `const char *` and
2536 * val_t)
2537 */
2538 v7_array_set(v7, get_args_v, 0, target_v);
2539 v7_array_set(v7, get_args_v, 1,
2540 v7_mk_string(v7, name_copy, name_len, 1));
2541
2542 /* call `get` callback */
2543 V7_TRY2(b_apply(v7, get_v, V7_UNDEFINED, get_args_v, 0, res),
2544 clean_proxy);
2545 } else {
2546 /*
2547 * there's no `get` callback: then, get property from the target object
2548 * (not from the proxy object)
2549 */
2550 V7_TRY2(v7_get_throwing(v7, target_v, name_copy, name_len, res),
2551 clean_proxy);
2552 }
2553
2554 clean_proxy:
2555
2556 free(name_copy);
2557
2558 v7_disown(v7, &get_args_v);
2559 v7_disown(v7, &get_v);
2560 v7_disown(v7, &name_v);
2561 v7_disown(v7, &handler_v);
2562 v7_disown(v7, &target_v);
2563 goto clean;
2564 }
2565 }
2566#endif
2567
2568 /* regular (non-proxy) property access */
2569 V7_TRY(
2570 v7_property_value(v7, obj, v7_get_property(v7, v, name, name_len), res));
2571
2572clean:
2573 v7_disown(v7, &v);
2574 return rcode;
2575}
2576
2577v7_val_t v7_get(struct v7 *v7, val_t obj, const char *name, size_t name_len) {
2578 enum v7_err rcode = V7_OK;
2579 uint8_t saved_is_thrown = 0;
2580 val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
2581 v7_val_t ret = V7_UNDEFINED;
2582
2583 rcode = v7_get_throwing(v7, obj, name, name_len, &ret);
2584 if (rcode != V7_OK) {
2585 rcode = V7_OK;
2586 if (saved_is_thrown) {
2587 rcode = v7_throw(v7, saved_thrown);
2588 } else {
2589 v7_clear_thrown_value(v7);
2590 }
2591 ret = V7_UNDEFINED;
2592 }
2593
2594 return ret;
2595}
2596
2597WARN_UNUSED_RESULT
2598V7_PRIVATE enum v7_err v7_get_throwing_v(struct v7 *v7, v7_val_t obj,
2599 v7_val_t name, v7_val_t *res) {
2600 enum v7_err rcode = V7_OK;
2601 size_t name_len;
2602 STATIC char buf[8];
2603 const char *s = buf;
2604 uint8_t fr = 0;
2605
2606 /* subscripting strings */
2607 if (v7_is_string(obj)) {
2608 char ch;
2609 double dch = 0;
2610
2611 rcode = v7_char_code_at(v7, obj, name, &dch);
2612 if (rcode != V7_OK) {
2613 goto clean;
2614 }
2615
2616 if (!isnan(dch)) {
2617 ch = dch;
2618 *res = v7_mk_string(v7, &ch, 1, 1);
2619 goto clean;
2620 }
2621 }
2622
2623 if (v7_is_string(name)) {
2624 s = v7_get_string(v7, &name, &name_len);
2625 } else {
2626 char *stmp;
2627 V7_TRY(v7_stringify_throwing(v7, name, buf, sizeof(buf),
2628 V7_STRINGIFY_DEFAULT, &stmp));
2629 s = stmp;
2630 if (s != buf) {
2631 fr = 1;
2632 }
2633 name_len = strlen(s);
2634 }
2635 V7_TRY(v7_get_throwing(v7, obj, s, name_len, res));
2636
2637clean:
2638 if (fr) {
2639 free((void *) s);
2640 }
2641 return rcode;
2642}
2643
2644V7_PRIVATE void v7_destroy_property(struct v7_property **p) {
2645 *p = NULL;
2646}
2647
2648WARN_UNUSED_RESULT
2649V7_PRIVATE enum v7_err v7_invoke_setter(struct v7 *v7, struct v7_property *prop,
2650 val_t obj, val_t val) {
2651 enum v7_err rcode = V7_OK;
2652 val_t setter = prop->value, args;
2653 v7_own(v7, &val);
2654 args = v7_mk_dense_array(v7);
2655 v7_own(v7, &args);
2656 if (prop->attributes & V7_PROPERTY_GETTER) {
2657 setter = v7_array_get(v7, prop->value, 1);
2658 }
2659 v7_array_set(v7, args, 0, val);
2660 v7_disown(v7, &args);
2661 v7_disown(v7, &val);
2662 {
2663 val_t val = V7_UNDEFINED;
2664 V7_TRY(b_apply(v7, setter, obj, args, 0, &val));
2665 }
2666
2667clean:
2668 return rcode;
2669}
2670
2671static v7_prop_attr_t apply_attrs_desc(v7_prop_attr_desc_t attrs_desc,
2672 v7_prop_attr_t old_attrs) {
2673 v7_prop_attr_t ret = old_attrs;
2674 if (old_attrs & V7_PROPERTY_NON_CONFIGURABLE) {
2675 /*
2676 * The property is non-configurable: we can only change it from being
2677 * writable to non-writable
2678 */
2679
2680 if ((attrs_desc >> _V7_DESC_SHIFT) & V7_PROPERTY_NON_WRITABLE &&
2681 (attrs_desc & V7_PROPERTY_NON_WRITABLE)) {
2682 ret |= V7_PROPERTY_NON_WRITABLE;
2683 }
2684
2685 } else {
2686 /* The property is configurable: we can change any attributes */
2687 ret = (old_attrs & ~(attrs_desc >> _V7_DESC_SHIFT)) |
2688 (attrs_desc & _V7_DESC_MASK);
2689 }
2690
2691 return ret;
2692}
2693
2694int v7_def(struct v7 *v7, val_t obj, const char *name, size_t len,
2695 v7_prop_attr_desc_t attrs_desc, v7_val_t val) {
2696 enum v7_err rcode = V7_OK;
2697 uint8_t saved_is_thrown = 0;
2698 val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
2699 int ret = -1;
2700
2701 {
2702 struct v7_property *tmp = NULL;
2703 rcode = def_property(v7, obj, name, len, attrs_desc, val, 0 /*not assign*/,
2704 &tmp);
2705 ret = (tmp == NULL) ? -1 : 0;
2706 }
2707
2708 if (rcode != V7_OK) {
2709 rcode = V7_OK;
2710 if (saved_is_thrown) {
2711 rcode = v7_throw(v7, saved_thrown);
2712 } else {
2713 v7_clear_thrown_value(v7);
2714 }
2715 ret = -1;
2716 }
2717
2718 return ret;
2719}
2720
2721int v7_set(struct v7 *v7, val_t obj, const char *name, size_t len,
2722 v7_val_t val) {
2723 enum v7_err rcode = V7_OK;
2724 uint8_t saved_is_thrown = 0;
2725 val_t saved_thrown = v7_get_thrown_value(v7, &saved_is_thrown);
2726 int ret = -1;
2727
2728 {
2729 struct v7_property *tmp = NULL;
2730 rcode = set_property(v7, obj, name, len, val, &tmp);
2731 ret = (tmp == NULL) ? -1 : 0;
2732 }
2733
2734 if (rcode != V7_OK) {
2735 rcode = V7_OK;
2736 if (saved_is_thrown) {
2737 rcode = v7_throw(v7, saved_thrown);
2738 } else {
2739 v7_clear_thrown_value(v7);
2740 }
2741 ret = -1;
2742 }
2743
2744 return ret;
2745}
2746
2747WARN_UNUSED_RESULT
2748V7_PRIVATE enum v7_err set_property_v(struct v7 *v7, val_t obj, val_t name,
2749 val_t val, struct v7_property **res) {
2750 return def_property_v(v7, obj, name, 0, val, 1 /*as_assign*/, res);
2751}
2752
2753WARN_UNUSED_RESULT
2754V7_PRIVATE enum v7_err set_property(struct v7 *v7, val_t obj, const char *name,
2755 size_t len, v7_val_t val,
2756 struct v7_property **res) {
2757 return def_property(v7, obj, name, len, 0, val, 1 /*as_assign*/, res);
2758}
2759
2760WARN_UNUSED_RESULT
2761V7_PRIVATE enum v7_err def_property_v(struct v7 *v7, val_t obj, val_t name,
2762 v7_prop_attr_desc_t attrs_desc, val_t val,
2763 uint8_t as_assign,
2764 struct v7_property **res) {
2765 enum v7_err rcode = V7_OK;
2766 struct v7_property *prop = NULL;
2767 size_t len;
2768 const char *n = v7_get_string(v7, &name, &len);
2769
2770 v7_own(v7, &name);
2771 v7_own(v7, &val);
2772
2773 if (!v7_is_object(obj)) {
2774 prop = NULL;
2775 goto clean;
2776 }
2777
2778#if V7_ENABLE__Proxy
2779 if ((get_object_struct(obj)->attributes & V7_OBJ_PROXY) &&
2780 !is_special_proxy_name(n, len)) {
2781 /* we need to access the target object through a proxy */
2782
2783 val_t target_v = V7_UNDEFINED;
2784 val_t handler_v = V7_UNDEFINED;
2785 val_t set_v = V7_UNDEFINED;
2786 val_t set_args_v = V7_UNDEFINED;
2787
2788 v7_own(v7, &target_v);
2789 v7_own(v7, &handler_v);
2790 v7_own(v7, &set_v);
2791 v7_own(v7, &set_args_v);
2792
2793 V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_TARGET_NAME, ~0, &target_v),
2794 clean_proxy);
2795 V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_HANDLER_NAME, ~0, &handler_v),
2796 clean_proxy);
2797 /*
2798 * We'll consult "set" property in case of the plain assignment only;
2799 * Object.defineProperty() has its own trap `defineProperty` which is not
2800 * yet implemented in v7
2801 */
2802 if (as_assign) {
2803 V7_TRY2(v7_get_throwing(v7, handler_v, "set", ~0, &set_v), clean_proxy);
2804 }
2805
2806 if (v7_is_callable(v7, set_v)) {
2807 /* The `set` callback is actually callable, so, use it */
2808
2809 /* prepare arguments for the callback */
2810 set_args_v = v7_mk_dense_array(v7);
2811 /*
2812 * TODO(dfrank): don't copy string in case we already have val_t
2813 * (we need some generic function which will take both const char * and
2814 * val_t for that)
2815 */
2816 v7_array_set(v7, set_args_v, 0, target_v);
2817 v7_array_set(v7, set_args_v, 1, name);
2818 v7_array_set(v7, set_args_v, 2, val);
2819
2820 /* call `set` callback */
2821 V7_TRY2(b_apply(v7, set_v, V7_UNDEFINED, set_args_v, 0, &val),
2822 clean_proxy);
2823
2824 /* in strict mode, we should throw if trap returned falsy value */
2825 if (is_strict_mode(v7) && !v7_is_truthy(v7, val)) {
2826 V7_THROW2(
2827 v7_throwf(v7, TYPE_ERROR, "Trap returned falsy for property '%s'",
2828 v7_get_string(v7, &name, NULL)),
2829 clean_proxy);
2830 }
2831
2832 } else {
2833 /*
2834 * there's no `set` callback: then, set property on the target object
2835 * (not on the proxy object)
2836 */
2837 V7_TRY2(
2838 def_property_v(v7, target_v, name, attrs_desc, val, as_assign, res),
2839 clean_proxy);
2840 }
2841
2842 clean_proxy:
2843 v7_disown(v7, &set_args_v);
2844 v7_disown(v7, &set_v);
2845 v7_disown(v7, &handler_v);
2846 v7_disown(v7, &target_v);
2847 goto clean;
2848 }
2849#endif
2850
2851 /* regular (non-proxy) property access */
2852 prop = v7_get_own_property(v7, obj, n, len);
2853 if (prop == NULL) {
2854 /*
2855 * The own property with given `name` doesn't exist yet: try to create it,
2856 * set requested `name` and `attributes`, and append to the object's
2857 * properties
2858 */
2859
2860 /* make sure the object is extensible */
2861 if (get_object_struct(obj)->attributes & V7_OBJ_NOT_EXTENSIBLE) {
2862 /*
2863 * We should throw if we use `Object.defineProperty`, or if we're in
2864 * strict mode.
2865 */
2866 if (is_strict_mode(v7) || !as_assign) {
2867 V7_THROW(v7_throwf(v7, TYPE_ERROR, "Object is not extensible"));
2868 }
2869 prop = NULL;
2870 goto clean;
2871 }
2872
2873 if ((prop = v7_mk_property(v7)) == NULL) {
2874 prop = NULL; /* LCOV_EXCL_LINE */
2875 goto clean;
2876 }
2877 prop->name = name;
2878 prop->value = val;
2879 prop->attributes = apply_attrs_desc(attrs_desc, V7_DEFAULT_PROPERTY_ATTRS);
2880
2881 prop->next = get_object_struct(obj)->properties;
2882 get_object_struct(obj)->properties = prop;
2883 goto clean;
2884 } else {
2885 /* Property already exists */
2886
2887 if (prop->attributes & V7_PROPERTY_NON_WRITABLE) {
2888 /* The property is read-only */
2889
2890 if (as_assign) {
2891 /* Plain assignment: in strict mode throw, otherwise ignore */
2892 if (is_strict_mode(v7)) {
2893 V7_THROW(
2894 v7_throwf(v7, TYPE_ERROR, "Cannot assign to read-only property"));
2895 } else {
2896 prop = NULL;
2897 goto clean;
2898 }
2899 } else if (prop->attributes & V7_PROPERTY_NON_CONFIGURABLE) {
2900 /*
2901 * Use `Object.defineProperty` semantic, and the property is
2902 * non-configurable: if no value is provided, or if new value is equal
2903 * to the existing one, then just fall through to change attributes;
2904 * otherwise, throw.
2905 */
2906
2907 if (!(attrs_desc & V7_DESC_PRESERVE_VALUE)) {
2908 uint8_t equal = 0;
2909 if (v7_is_string(val) && v7_is_string(prop->value)) {
2910 equal = (s_cmp(v7, val, prop->value) == 0);
2911 } else {
2912 equal = (val == prop->value);
2913 }
2914
2915 if (!equal) {
2916 /* Values are not equal: should throw */
2917 V7_THROW(v7_throwf(v7, TYPE_ERROR,
2918 "Cannot redefine read-only property"));
2919 } else {
2920 /*
2921 * Values are equal. Will fall through so that attributes might
2922 * change.
2923 */
2924 }
2925 } else {
2926 /*
2927 * No value is provided. Will fall through so that attributes might
2928 * change.
2929 */
2930 }
2931 } else {
2932 /*
2933 * Use `Object.defineProperty` semantic, and the property is
2934 * configurable: will fall through and assign new value, effectively
2935 * ignoring non-writable flag. This is the same as making a property
2936 * writable, then assigning a new value, and making a property
2937 * non-writable again.
2938 */
2939 }
2940 } else if (prop->attributes & V7_PROPERTY_SETTER) {
2941 /* Invoke setter */
2942 V7_TRY(v7_invoke_setter(v7, prop, obj, val));
2943 prop = NULL;
2944 goto clean;
2945 }
2946
2947 /* Set value and apply attrs delta */
2948 if (!(attrs_desc & V7_DESC_PRESERVE_VALUE)) {
2949 prop->value = val;
2950 }
2951 prop->attributes = apply_attrs_desc(attrs_desc, prop->attributes);
2952 }
2953
2954clean:
2955
2956 if (res != NULL) {
2957 *res = prop;
2958 }
2959
2960 v7_disown(v7, &val);
2961 v7_disown(v7, &name);
2962
2963 return rcode;
2964}
2965
2966WARN_UNUSED_RESULT
2967V7_PRIVATE enum v7_err def_property(struct v7 *v7, val_t obj, const char *name,
2968 size_t len, v7_prop_attr_desc_t attrs_desc,
2969 v7_val_t val, uint8_t as_assign,
2970 struct v7_property **res) {
2971 enum v7_err rcode = V7_OK;
2972 val_t name_val = V7_UNDEFINED;
2973
2974 v7_own(v7, &obj);
2975 v7_own(v7, &val);
2976 v7_own(v7, &name_val);
2977
2978 if (len == (size_t) ~0) {
2979 len = strlen(name);
2980 }
2981
2982 name_val = v7_mk_string(v7, name, len, 1);
2983 V7_TRY(def_property_v(v7, obj, name_val, attrs_desc, val, as_assign, res));
2984
2985clean:
2986 v7_disown(v7, &name_val);
2987 v7_disown(v7, &val);
2988 v7_disown(v7, &obj);
2989
2990 return rcode;
2991}
2992
2993V7_PRIVATE int set_method(struct v7 *v7, v7_val_t obj, const char *name,
2994 v7_cfunction_t *func, int num_args) {
2995 return v7_def(v7, obj, name, strlen(name), V7_DESC_ENUMERABLE(0),
2996 mk_cfunction_obj(v7, func, num_args));
2997}
2998
2999int v7_set_method(struct v7 *v7, v7_val_t obj, const char *name,
3000 v7_cfunction_t *func) {
3001 return set_method(v7, obj, name, func, ~0);
3002}
3003
3004V7_PRIVATE int set_cfunc_prop(struct v7 *v7, val_t o, const char *name,
3005 v7_cfunction_t *f) {
3006 return v7_def(v7, o, name, strlen(name), V7_DESC_ENUMERABLE(0),
3007 v7_mk_cfunction(f));
3008}
3009
3010/*
3011 * See comments in `object_public.h`
3012 */
3013int v7_del(struct v7 *v7, val_t obj, const char *name, size_t len) {
3014 struct v7_property *prop, *prev;
3015
3016 if (!v7_is_object(obj)) {
3017 return -1;
3018 }
3019 if (len == (size_t) ~0) {
3020 len = strlen(name);
3021 }
3022 for (prev = NULL, prop = get_object_struct(obj)->properties; prop != NULL;
3023 prev = prop, prop = prop->next) {
3024 size_t n;
3025 const char *s = v7_get_string(v7, &prop->name, &n);
3026 if (n == len && strncmp(s, name, len) == 0) {
3027 if (prev) {
3028 prev->next = prop->next;
3029 } else {
3030 get_object_struct(obj)->properties = prop->next;
3031 }
3032 v7_destroy_property(&prop);
3033 return 0;
3034 }
3035 }
3036 return -1;
3037}
3038
3039WARN_UNUSED_RESULT
3040V7_PRIVATE enum v7_err v7_property_value(struct v7 *v7, val_t obj,
3041 struct v7_property *p, val_t *res) {
3042 enum v7_err rcode = V7_OK;
3043 if (p == NULL) {
3044 *res = V7_UNDEFINED;
3045 goto clean;
3046 }
3047 if (p->attributes & V7_PROPERTY_GETTER) {
3048 val_t getter = p->value;
3049 if (p->attributes & V7_PROPERTY_SETTER) {
3050 getter = v7_array_get(v7, p->value, 0);
3051 }
3052 {
3053 V7_TRY(b_apply(v7, getter, obj, V7_UNDEFINED, 0, res));
3054 goto clean;
3055 }
3056 }
3057
3058 *res = p->value;
3059 goto clean;
3060
3061clean:
3062 return rcode;
3063}
3064
3065enum v7_err v7_init_prop_iter_ctx(struct v7 *v7, v7_val_t obj,
3066 struct prop_iter_ctx *ctx) {
3067 return init_prop_iter_ctx(v7, obj, 1 /*proxy-transparent*/, ctx);
3068}
3069
3070WARN_UNUSED_RESULT
3071V7_PRIVATE enum v7_err init_prop_iter_ctx(struct v7 *v7, v7_val_t obj,
3072 int proxy_transp,
3073 struct prop_iter_ctx *ctx) {
3074 enum v7_err rcode = V7_OK;
3075
3076 v7_own(v7, &obj);
3077
3078 memset(ctx, 0x00, sizeof(*ctx));
3079
3080 if (v7_is_object(obj)) {
3081#if V7_ENABLE__Proxy
3082 if (proxy_transp && get_object_struct(obj)->attributes & V7_OBJ_PROXY) {
3083 v7_val_t ownKeys_v = V7_UNDEFINED;
3084 v7_val_t args_v = V7_UNDEFINED;
3085
3086 v7_own(v7, &ownKeys_v);
3087 v7_own(v7, &args_v);
3088
3089 ctx->proxy_ctx =
3090 (struct prop_iter_proxy_ctx *) calloc(1, sizeof(*ctx->proxy_ctx));
3091
3092 ctx->proxy_ctx->target_obj = V7_UNDEFINED;
3093 ctx->proxy_ctx->handler_obj = V7_UNDEFINED;
3094 ctx->proxy_ctx->own_keys = V7_UNDEFINED;
3095 ctx->proxy_ctx->get_own_prop_desc = V7_UNDEFINED;
3096
3097 v7_own(v7, &ctx->proxy_ctx->target_obj);
3098 v7_own(v7, &ctx->proxy_ctx->handler_obj);
3099 v7_own(v7, &ctx->proxy_ctx->own_keys);
3100 v7_own(v7, &ctx->proxy_ctx->get_own_prop_desc);
3101
3102 V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_TARGET_NAME, ~0,
3103 &ctx->proxy_ctx->target_obj),
3104 clean_proxy);
3105 V7_TRY2(v7_get_throwing(v7, obj, _V7_PROXY_HANDLER_NAME, ~0,
3106 &ctx->proxy_ctx->handler_obj),
3107 clean_proxy);
3108
3109 V7_TRY2(v7_get_throwing(v7, ctx->proxy_ctx->handler_obj, "ownKeys", ~0,
3110 &ownKeys_v),
3111 clean_proxy);
3112
3113 if (v7_is_callable(v7, ownKeys_v)) {
3114 /* prepare arguments for the ownKeys callback */
3115 args_v = v7_mk_dense_array(v7);
3116 v7_array_set(v7, args_v, 0, ctx->proxy_ctx->target_obj);
3117
3118 /* call `ownKeys` callback, and save the result in context */
3119 V7_TRY2(b_apply(v7, ownKeys_v, V7_UNDEFINED, args_v, 0,
3120 &ctx->proxy_ctx->own_keys),
3121 clean_proxy);
3122
3123 ctx->proxy_ctx->has_own_keys = 1;
3124 ctx->proxy_ctx->own_key_idx = 0;
3125
3126 } else {
3127 /*
3128 * No ownKeys callback, so we'll iterate real properties of the target
3129 * object
3130 */
3131
3132 /*
3133 * TODO(dfrank): add support for the target object which is a proxy as
3134 * well
3135 */
3136 ctx->cur_prop =
3137 get_object_struct(ctx->proxy_ctx->target_obj)->properties;
3138 }
3139
3140 V7_TRY2(v7_get_throwing(v7, ctx->proxy_ctx->handler_obj, "_gpdc", ~0,
3141 &ctx->proxy_ctx->get_own_prop_desc),
3142 clean_proxy);
3143 if (v7_is_foreign(ctx->proxy_ctx->get_own_prop_desc)) {
3144 /*
3145 * C callback for getting property descriptor is provided: will use it
3146 */
3147 ctx->proxy_ctx->has_get_own_prop_desc = 1;
3148 ctx->proxy_ctx->has_get_own_prop_desc_C = 1;
3149 } else {
3150 /*
3151 * No C callback for getting property descriptor is provided, let's
3152 * check if there is a JS one..
3153 */
3154 V7_TRY2(v7_get_throwing(v7, ctx->proxy_ctx->handler_obj,
3155 "getOwnPropertyDescriptor", ~0,
3156 &ctx->proxy_ctx->get_own_prop_desc),
3157 clean_proxy);
3158
3159 if (v7_is_callable(v7, ctx->proxy_ctx->get_own_prop_desc)) {
3160 /* Yes there is, we'll use it */
3161 ctx->proxy_ctx->has_get_own_prop_desc = 1;
3162 }
3163 }
3164
3165 clean_proxy:
3166 v7_disown(v7, &args_v);
3167 v7_disown(v7, &ownKeys_v);
3168
3169 if (rcode != V7_OK) {
3170 /* something went wrong, so, disown values in the context and free it */
3171 v7_disown(v7, &ctx->proxy_ctx->get_own_prop_desc);
3172 v7_disown(v7, &ctx->proxy_ctx->own_keys);
3173 v7_disown(v7, &ctx->proxy_ctx->handler_obj);
3174 v7_disown(v7, &ctx->proxy_ctx->target_obj);
3175
3176 free(ctx->proxy_ctx);
3177 ctx->proxy_ctx = NULL;
3178
3179 goto clean;
3180 }
3181 } else {
3182#else
3183 (void) proxy_transp;
3184#endif
3185
3186 /* Object is not a proxy: we'll iterate real properties */
3187 ctx->cur_prop = get_object_struct(obj)->properties;
3188
3189#if V7_ENABLE__Proxy
3190 }
3191#endif
3192 }
3193
3194#if V7_ENABLE__Proxy
3195clean:
3196#endif
3197 v7_disown(v7, &obj);
3198 if (rcode == V7_OK) {
3199 ctx->init = 1;
3200 }
3201 return rcode;
3202}
3203
3204void v7_destruct_prop_iter_ctx(struct v7 *v7, struct prop_iter_ctx *ctx) {
3205 if (ctx->init) {
3206#if V7_ENABLE__Proxy
3207 if (ctx->proxy_ctx != NULL) {
3208 v7_disown(v7, &ctx->proxy_ctx->target_obj);
3209 v7_disown(v7, &ctx->proxy_ctx->handler_obj);
3210 v7_disown(v7, &ctx->proxy_ctx->own_keys);
3211 v7_disown(v7, &ctx->proxy_ctx->get_own_prop_desc);
3212 }
3213 free(ctx->proxy_ctx);
3214 ctx->proxy_ctx = NULL;
3215#else
3216 (void) v7;
3217#endif
3218 ctx->init = 0;
3219 }
3220}
3221
3222int v7_next_prop(struct v7 *v7, struct prop_iter_ctx *ctx, v7_val_t *name,
3223 v7_val_t *value, v7_prop_attr_t *attrs) {
3224 int ok = 0;
3225 if (next_prop(v7, ctx, name, value, attrs, &ok) != V7_OK) {
3226 fprintf(stderr, "next_prop failed\n");
3227 ok = 0;
3228 }
3229 return ok;
3230}
3231
3232#if V7_ENABLE__Proxy
3233WARN_UNUSED_RESULT
3234static enum v7_err get_custom_prop_desc(struct v7 *v7, v7_val_t name,
3235 struct prop_iter_ctx *ctx,
3236 struct v7_property *res_prop, int *ok) {
3237 enum v7_err rcode = V7_OK;
3238
3239 v7_val_t args_v = V7_UNDEFINED;
3240 v7_val_t desc_v = V7_UNDEFINED;
3241 v7_val_t tmpflag_v = V7_UNDEFINED;
3242
3243 v7_own(v7, &name);
3244 v7_own(v7, &args_v);
3245 v7_own(v7, &desc_v);
3246 v7_own(v7, &tmpflag_v);
3247
3248 *ok = 0;
3249
3250 if (ctx->proxy_ctx->has_get_own_prop_desc_C) {
3251 /*
3252 * There is a C callback which should fill the property descriptor
3253 * structure, see `v7_get_own_prop_desc_cb_t`
3254 */
3255 v7_get_own_prop_desc_cb_t *cb = NULL;
3256 memset(res_prop, 0, sizeof(*res_prop));
3257 cb = (v7_get_own_prop_desc_cb_t *) v7_get_ptr(
3258 v7, ctx->proxy_ctx->get_own_prop_desc);
3259
3260 res_prop->attributes = 0;
3261 res_prop->value = V7_UNDEFINED;
3262
3263 *ok = !!cb(v7, ctx->proxy_ctx->target_obj, name, &res_prop->attributes,
3264 &res_prop->value);
3265 } else {
3266 /* prepare arguments for the getOwnPropertyDescriptor callback */
3267 args_v = v7_mk_dense_array(v7);
3268 v7_array_set(v7, args_v, 0, ctx->proxy_ctx->target_obj);
3269 v7_array_set(v7, args_v, 1, name);
3270
3271 /* call getOwnPropertyDescriptor callback */
3272 V7_TRY(b_apply(v7, ctx->proxy_ctx->get_own_prop_desc, V7_UNDEFINED, args_v,
3273 0, &desc_v));
3274
3275 if (v7_is_object(desc_v)) {
3276 res_prop->attributes = 0;
3277
3278 V7_TRY(v7_get_throwing(v7, desc_v, "writable", ~0, &tmpflag_v));
3279 if (!v7_is_truthy(v7, tmpflag_v)) {
3280 res_prop->attributes |= V7_PROPERTY_NON_WRITABLE;
3281 }
3282
3283 V7_TRY(v7_get_throwing(v7, desc_v, "configurable", ~0, &tmpflag_v));
3284 if (!v7_is_truthy(v7, tmpflag_v)) {
3285 res_prop->attributes |= V7_PROPERTY_NON_CONFIGURABLE;
3286 }
3287
3288 V7_TRY(v7_get_throwing(v7, desc_v, "enumerable", ~0, &tmpflag_v));
3289 if (!v7_is_truthy(v7, tmpflag_v)) {
3290 res_prop->attributes |= V7_PROPERTY_NON_ENUMERABLE;
3291 }
3292
3293 V7_TRY(v7_get_throwing(v7, desc_v, "value", ~0, &res_prop->value));
3294
3295 *ok = 1;
3296 }
3297 }
3298
3299 /* We always set the name in the property descriptor to the actual name */
3300 res_prop->name = name;
3301
3302clean:
3303 v7_disown(v7, &tmpflag_v);
3304 v7_disown(v7, &desc_v);
3305 v7_disown(v7, &args_v);
3306 v7_disown(v7, &name);
3307
3308 return rcode;
3309}
3310#endif
3311
3312WARN_UNUSED_RESULT
3313V7_PRIVATE enum v7_err next_prop(struct v7 *v7, struct prop_iter_ctx *ctx,
3314 v7_val_t *name, v7_val_t *value,
3315 v7_prop_attr_t *attrs, int *ok) {
3316 enum v7_err rcode = V7_OK;
3317 struct v7_property p;
3318
3319 (void) v7;
3320
3321 memset(&p, 0, sizeof(p));
3322 p.name = V7_UNDEFINED;
3323 p.value = V7_UNDEFINED;
3324
3325 v7_own(v7, &p.name);
3326 v7_own(v7, &p.value);
3327
3328 assert(ctx->init);
3329
3330 *ok = 0;
3331
3332#if V7_ENABLE__Proxy
3333 if (ctx->proxy_ctx == NULL || !ctx->proxy_ctx->has_own_keys) {
3334 /*
3335 * No `ownKeys` callback, so we'll iterate real properties of the object
3336 * (either the given object or, if it's a proxy, the proxy's target object)
3337 */
3338
3339 if (ctx->cur_prop != NULL) {
3340 if (ctx->proxy_ctx == NULL || !ctx->proxy_ctx->has_get_own_prop_desc) {
3341 /*
3342 * There is no `getOwnPropertyDescriptor` callback, so, use the current
3343 * real property
3344 */
3345 memcpy(&p, ctx->cur_prop, sizeof(p));
3346 *ok = 1;
3347 } else {
3348 /*
3349 * There is a `getOwnPropertyDescriptor` callback, so call it for the
3350 * name of the current real property
3351 */
3352 V7_TRY(get_custom_prop_desc(v7, ctx->cur_prop->name, ctx, &p, ok));
3353 }
3354
3355 ctx->cur_prop = ctx->cur_prop->next;
3356 }
3357 } else {
3358 /* We have custom own keys */
3359 v7_val_t cur_key = V7_UNDEFINED;
3360 size_t len = v7_array_length(v7, ctx->proxy_ctx->own_keys);
3361
3362 v7_own(v7, &cur_key);
3363
3364 /*
3365 * Iterate through the custom own keys until we can get the proper property
3366 * descriptor for the given key
3367 */
3368 while (!*ok && (size_t) ctx->proxy_ctx->own_key_idx < len) {
3369 cur_key = v7_array_get(v7, ctx->proxy_ctx->own_keys,
3370 ctx->proxy_ctx->own_key_idx);
3371 ctx->proxy_ctx->own_key_idx++;
3372
3373 if (ctx->proxy_ctx->has_get_own_prop_desc) {
3374 /*
3375 * There is a `getOwnPropertyDescriptor` callback, so, call it for the
3376 * current custom key and get all descriptor data from the object
3377 * returned. The `ok` variable will be updated appropriately (it will
3378 * be 0 if the callback did not return a proper descriptor)
3379 */
3380 V7_TRY2(get_custom_prop_desc(v7, cur_key, ctx, &p, ok), clean_custom);
3381 } else {
3382 /*
3383 * There is no `getOwnPropertyDescriptor` callback, so, try to get
3384 * real property with the name equal to the current key
3385 */
3386 size_t len = 0;
3387 const char *name = v7_get_string(v7, &cur_key, &len);
3388
3389 struct v7_property *real_prop =
3390 v7_get_own_property(v7, ctx->proxy_ctx->target_obj, name, len);
3391 if (real_prop != NULL) {
3392 /* Property exists, so use data from its descriptor */
3393 memcpy(&p, real_prop, sizeof(p));
3394 *ok = 1;
3395 }
3396 }
3397 }
3398 clean_custom:
3399 v7_disown(v7, &cur_key);
3400 if (rcode != V7_OK) {
3401 goto clean;
3402 }
3403 }
3404
3405#else
3406 /*
3407 * Proxy is disabled: just get the next property
3408 */
3409 if (ctx->cur_prop != NULL) {
3410 memcpy(&p, ctx->cur_prop, sizeof(p));
3411 *ok = 1;
3412 ctx->cur_prop = ctx->cur_prop->next;
3413 }
3414#endif
3415
3416 /* If we have a valid property descriptor, use data from it */
3417 if (*ok) {
3418 if (name != NULL) *name = p.name;
3419 if (value != NULL) *value = p.value;
3420 if (attrs != NULL) *attrs = p.attributes;
3421 }
3422
3423#if V7_ENABLE__Proxy
3424clean:
3425#endif
3426 v7_disown(v7, &p.value);
3427 v7_disown(v7, &p.name);
3428 return rcode;
3429}
3430
3431/* }}} Object properties */
3432
3433/* Object prototypes {{{ */
3434
3435V7_PRIVATE int obj_prototype_set(struct v7 *v7, struct v7_object *obj,
3436 struct v7_object *proto) {
3437 int ret = -1;
3438 (void) v7;
3439
3440 if (obj->attributes & V7_OBJ_FUNCTION) {
3441 ret = -1;
3442 } else {
3443 ((struct v7_generic_object *) obj)->prototype = proto;
3444 ret = 0;
3445 }
3446
3447 return ret;
3448}
3449
3450V7_PRIVATE struct v7_object *obj_prototype(struct v7 *v7,
3451 struct v7_object *obj) {
3452 if (obj->attributes & V7_OBJ_FUNCTION) {
3453 return get_object_struct(v7->vals.function_prototype);
3454 } else {
3455 return ((struct v7_generic_object *) obj)->prototype;
3456 }
3457}
3458
3459V7_PRIVATE int is_prototype_of(struct v7 *v7, val_t o, val_t p) {
3460 if (!v7_is_object(o) || !v7_is_object(p)) {
3461 return 0;
3462 }
3463
3464 /* walk the prototype chain */
3465 for (; !v7_is_null(o); o = v7_get_proto(v7, o)) {
3466 if (v7_get_proto(v7, o) == p) {
3467 return 1;
3468 }
3469 }
3470 return 0;
3471}
3472
3473int v7_is_instanceof(struct v7 *v7, val_t o, const char *c) {
3474 return v7_is_instanceof_v(v7, o, v7_get(v7, v7->vals.global_object, c, ~0));
3475}
3476
3477int v7_is_instanceof_v(struct v7 *v7, val_t o, val_t c) {
3478 return is_prototype_of(v7, o, v7_get(v7, c, "prototype", 9));
3479}
3480
3481v7_val_t v7_set_proto(struct v7 *v7, v7_val_t obj, v7_val_t proto) {
3482 if (v7_is_generic_object(obj)) {
3483 v7_val_t old_proto =
3484 v7_object_to_value(obj_prototype(v7, get_object_struct(obj)));
3485 obj_prototype_set(v7, get_object_struct(obj), get_object_struct(proto));
3486 return old_proto;
3487 } else {
3488 return V7_UNDEFINED;
3489 }
3490}
3491
3492val_t v7_get_proto(struct v7 *v7, val_t obj) {
3493 /*
3494 * NOTE: we don't use v7_is_callable() here, because it involves walking
3495 * through the object's properties, which may be expensive. And it's done
3496 * anyway for cfunction objects as it would for any other generic objects by
3497 * the call to `obj_prototype()`.
3498 *
3499 * Since this function is called quite often (at least, GC walks the
3500 * prototype chain), it's better to just handle cfunction objects as generic
3501 * objects.
3502 */
3503 if (is_js_function(obj) || is_cfunction_lite(obj)) {
3504 return v7->vals.function_prototype;
3505 }
3506 return v7_object_to_value(obj_prototype(v7, get_object_struct(obj)));
3507}
3508
3509V7_PRIVATE struct v7_property *get_user_data_property(v7_val_t obj) {
3510 struct v7_property *p;
3511 struct v7_object *o;
3512 if (!v7_is_object(obj)) return NULL;
3513 o = get_object_struct(obj);
3514
3515 for (p = o->properties; p != NULL; p = p->next) {
3516 if (p->attributes & _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR) {
3517 return p;
3518 }
3519 }
3520
3521 return NULL;
3522}
3523
3524/*
3525 * Returns the user data property structure associated with obj, or NULL if
3526 * `obj` is not an object.
3527 */
3528static struct v7_property *get_or_create_user_data_property(struct v7 *v7,
3529 v7_val_t obj) {
3530 struct v7_property *p = get_user_data_property(obj);
3531 struct v7_object *o;
3532
3533 if (p != NULL) return p;
3534
3535 if (!v7_is_object(obj)) return NULL;
3536 o = get_object_struct(obj);
3537 v7_own(v7, &obj);
3538 p = v7_mk_property(v7);
3539 v7_disown(v7, &obj);
3540
3541 p->attributes |= _V7_PROPERTY_USER_DATA_AND_DESTRUCTOR | _V7_PROPERTY_HIDDEN;
3542
3543 p->next = o->properties;
3544 o->properties = p;
3545
3546 return p;
3547}
3548
3549void v7_set_user_data(struct v7 *v7, v7_val_t obj, void *ud) {
3550 struct v7_property *p = get_or_create_user_data_property(v7, obj);
3551 if (p == NULL) return;
3552 p->value = v7_mk_foreign(v7, ud);
3553}
3554
3555void *v7_get_user_data(struct v7 *v7, v7_val_t obj) {
3556 struct v7_property *p = get_user_data_property(obj);
3557 (void) v7;
3558 if (p == NULL) return NULL;
3559 return v7_get_ptr(v7, p->value);
3560}
3561
3562void v7_set_destructor_cb(struct v7 *v7, v7_val_t obj, v7_destructor_cb_t *d) {
3563 struct v7_property *p = get_or_create_user_data_property(v7, obj);
3564 struct v7_object *o;
3565 union {
3566 void *v;
3567 v7_destructor_cb_t *f;
3568 } fu;
3569
3570 if (p == NULL) return;
3571
3572 o = get_object_struct(obj);
3573 if (d != NULL) {
3574 o->attributes |= V7_OBJ_HAS_DESTRUCTOR;
3575 fu.f = d;
3576 p->name = v7_mk_foreign(v7, fu.v);
3577 } else {
3578 o->attributes &= ~V7_OBJ_HAS_DESTRUCTOR;
3579 p->name = V7_UNDEFINED;
3580 }
3581}
3582
3583/* }}} Object prototypes */
3584#ifdef V7_MODULE_LINES
3585#line 1 "v7/src/regexp.c"
3586#endif
3587/*
3588 * Copyright (c) 2014 Cesanta Software Limited
3589 * All rights reserved
3590 */
3591
3592/* Amalgamated: #include "v7/src/internal.h" */
3593/* Amalgamated: #include "v7/src/core.h" */
3594/* Amalgamated: #include "v7/src/primitive.h" */
3595/* Amalgamated: #include "v7/src/object.h" */
3596/* Amalgamated: #include "v7/src/regexp.h" */
3597/* Amalgamated: #include "v7/src/exceptions.h" */
3598/* Amalgamated: #include "v7/src/string.h" */
3599/* Amalgamated: #include "v7/src/slre.h" */
3600
3601#if V7_ENABLE__RegExp
3602enum v7_err v7_mk_regexp(struct v7 *v7, const char *re, size_t re_len,
3603 const char *flags, size_t flags_len, v7_val_t *res) {
3604 enum v7_err rcode = V7_OK;
3605 struct slre_prog *p = NULL;
3606 struct v7_regexp *rp;
3607
3608 if (re_len == ~((size_t) 0)) re_len = strlen(re);
3609
3610 if (slre_compile(re, re_len, flags, flags_len, &p, 1) != SLRE_OK ||
3611 p == NULL) {
3612 rcode = v7_throwf(v7, TYPE_ERROR, "Invalid regex");
3613 goto clean;
3614 } else {
3615 *res = mk_object(v7, v7->vals.regexp_prototype);
3616 rp = (struct v7_regexp *) malloc(sizeof(*rp));
3617 rp->regexp_string = v7_mk_string(v7, re, re_len, 1);
3618 v7_own(v7, &rp->regexp_string);
3619 rp->compiled_regexp = p;
3620 rp->lastIndex = 0;
3621
3622 v7_def(v7, *res, "", 0, _V7_DESC_HIDDEN(1),
3623 pointer_to_value(rp) | V7_TAG_REGEXP);
3624 }
3625
3626clean:
3627 return rcode;
3628}
3629
3630V7_PRIVATE struct v7_regexp *v7_get_regexp_struct(struct v7 *v7, val_t v) {
3631 struct v7_property *p;
3632 int is = v7_is_regexp(v7, v);
3633 (void) is;
3634 assert(is == 1);
3635 /* TODO(mkm): make regexp use user data API */
3636 p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
3637 assert(p != NULL);
3638 return (struct v7_regexp *) get_ptr(p->value);
3639}
3640
3641int v7_is_regexp(struct v7 *v7, val_t v) {
3642 struct v7_property *p;
3643 if (!v7_is_generic_object(v)) return 0;
3644 /* TODO(mkm): make regexp use user data API */
3645 p = v7_get_own_property2(v7, v, "", 0, _V7_PROPERTY_HIDDEN);
3646 if (p == NULL) return 0;
3647 return (p->value & V7_TAG_MASK) == V7_TAG_REGEXP;
3648}