MIRROR: javascript for 馃悳's, a tiny runtime with big ambitions
1#ifndef SILVER_ENGINE_H
2#define SILVER_ENGINE_H
3
4#include "silver/vm.h"
5#include "internal.h"
6#include "runtime.h"
7#include "errors.h"
8#include "gc/objects.h"
9#include "modules/timer.h"
10
11#include <stdbool.h>
12#include <stdint.h>
13#include <stdlib.h>
14#include <string.h>
15#include <stdio.h>
16
17typedef enum {
18#define OP_DEF(name, size, n_pop, n_push, f) OP_##name,
19#include "silver/opcode.h"
20 OP__COUNT
21} sv_op_t;
22
23static const uint8_t sv_op_size[OP__COUNT] = {
24#define OP_DEF(name, size, n_pop, n_push, f) [OP_##name] = (size),
25#include "silver/opcode.h"
26};
27
28typedef struct {
29 const char *str;
30 uint32_t len;
31} sv_atom_t;
32
33typedef struct {
34 uint16_t index;
35 bool is_local;
36 bool is_const;
37} sv_upval_desc_t;
38
39typedef struct {
40 uint32_t bc_offset;
41 uint32_t line;
42 uint32_t col;
43 uint32_t src_off;
44 uint32_t src_end;
45} sv_srcpos_t;
46
47typedef enum {
48 SV_TI_UNKNOWN = 0,
49 SV_TI_NUM,
50 SV_TI_STR,
51 SV_TI_ARR,
52 SV_TI_OBJ,
53 SV_TI_BOOL,
54 SV_TI_NULL,
55 SV_TI_UNDEF,
56} sv_local_type_t;
57
58typedef struct {
59 uint8_t type;
60} sv_type_info_t;
61
62typedef struct {
63 ant_shape_t *cached_shape;
64 ant_object_t *cached_holder;
65 uint32_t cached_index;
66 uint32_t epoch;
67 uintptr_t cached_aux;
68 ant_shape_t *add_from_shape;
69 ant_shape_t *add_to_shape;
70 uint32_t add_slot;
71 uint32_t add_epoch;
72 bool cached_is_own;
73} sv_ic_entry_t;
74
75typedef struct {
76 uint32_t bc_off;
77 ant_shape_t *shared_shape;
78} sv_obj_site_cache_t;
79
80#define SV_GF_IC_AUX_WARMUP_MASK ((uintptr_t)0xFFu)
81#define SV_GF_IC_AUX_MISS_MASK ((uintptr_t)0xFF00u)
82#define SV_GF_IC_AUX_ACTIVE_BIT ((uintptr_t)0x10000u)
83#define SV_GF_IC_AUX_MISS_SHIFT 8u
84
85#define SV_GF_IC_WARMUP_ENABLE 16u
86#define SV_GF_IC_MISS_DISABLE 4u
87
88static inline uint8_t sv_gf_ic_warmup(uintptr_t aux) {
89 return (uint8_t)(aux & SV_GF_IC_AUX_WARMUP_MASK);
90}
91
92static inline uint8_t sv_gf_ic_miss_streak(uintptr_t aux) {
93 return (uint8_t)((aux & SV_GF_IC_AUX_MISS_MASK) >> SV_GF_IC_AUX_MISS_SHIFT);
94}
95
96static inline bool sv_gf_ic_active(uintptr_t aux) {
97 return (aux & SV_GF_IC_AUX_ACTIVE_BIT) != 0;
98}
99
100static inline uintptr_t sv_gf_ic_pack_aux(uint8_t warmup, uint8_t miss_streak, bool active) {
101 uintptr_t aux = ((uintptr_t)warmup & SV_GF_IC_AUX_WARMUP_MASK) |
102 ((uintptr_t)miss_streak << SV_GF_IC_AUX_MISS_SHIFT);
103 if (active) aux |= SV_GF_IC_AUX_ACTIVE_BIT;
104 return aux;
105}
106
107bool sv_lookup_srcpos(sv_func_t *func, int bc_offset, uint32_t *line, uint32_t *col);
108bool sv_lookup_srcspan(sv_func_t *func, int bc_offset, uint32_t *src_off, uint32_t *src_end);
109
110#ifdef ANT_JIT
111typedef struct {
112 uint16_t bc_off;
113 uint8_t miss_count;
114 uint8_t disabled;
115 sv_func_t *target;
116} sv_call_target_fb_t;
117#endif
118
119struct sv_func {
120 uint8_t *code;
121 int code_len;
122
123 ant_value_t *constants;
124 int const_count;
125
126 struct sv_func **child_funcs;
127 int child_func_count;
128
129 uint32_t *gc_const_slots;
130 int gc_const_slot_count;
131
132 sv_atom_t *atoms;
133 int atom_count;
134
135 sv_ic_entry_t *ic_slots;
136 uint16_t ic_count;
137
138 sv_obj_site_cache_t *obj_sites;
139 uint16_t obj_site_count;
140
141 sv_upval_desc_t *upval_descs;
142 int max_locals;
143 int max_stack;
144
145 sv_type_info_t *local_types;
146 int local_type_count;
147 sv_type_info_t *param_hints;
148 int param_hint_count;
149 uint8_t return_hint;
150
151 int param_count;
152 int upvalue_count;
153
154 bool is_strict;
155 bool is_arrow;
156 bool is_async;
157 bool has_await;
158 bool is_generator;
159 bool is_method;
160 bool is_static;
161 bool is_tla;
162 uint64_t gc_epoch;
163
164 const char *name;
165 const char *filename;
166
167 sv_srcpos_t *srcpos;
168 int srcpos_count;
169 int source_line;
170
171 const char *source;
172 int source_len;
173 int source_start;
174 int source_end;
175
176#ifdef ANT_JIT
177 void *jit_code;
178
179 uint32_t call_count;
180 uint32_t back_edge_count;
181
182 bool jit_compile_failed;
183 bool jit_compiling;
184 uint32_t tfb_version;
185 uint32_t jit_compiled_tfb_ver;
186 uint8_t *type_feedback;
187 uint8_t *local_type_feedback;
188 uint8_t *param_type_feedback;
189 uint64_t ctor_prop_samples;
190 uint64_t ctor_prop_hist[17];
191 uint8_t ctor_inobj_limit;
192 uint8_t ctor_inobj_frozen;
193
194 sv_call_target_fb_t *call_target_fb;
195 uint8_t call_target_fb_count;
196#endif
197};
198
199typedef enum {
200 SV_COMPLETION_NONE = 0,
201 SV_COMPLETION_THROW = 1,
202 SV_COMPLETION_RETURN = 2,
203} sv_completion_kind_t;
204
205typedef struct {
206 sv_completion_kind_t kind;
207 ant_value_t value;
208} sv_completion_t;
209
210typedef enum {
211 SV_RESUME_NEXT = 0,
212 SV_RESUME_THROW = 1,
213 SV_RESUME_RETURN = 2,
214} sv_resume_kind_t;
215
216typedef struct
217 sv_upvalue sv_upvalue_t;
218
219typedef struct sv_frame {
220 uint8_t *ip;
221 ant_value_t *bp;
222 ant_value_t *lp;
223
224 sv_func_t *func;
225 ant_value_t callee;
226 ant_value_t this;
227 ant_value_t new_target;
228 ant_value_t super_val;
229
230 int prev_sp;
231 int handler_base;
232 int handler_top;
233 int argc;
234
235 sv_completion_t completion;
236 sv_upvalue_t **upvalues;
237 int upvalue_count;
238
239 ant_value_t with_obj;
240 ant_value_t arguments_obj;
241} sv_frame_t;
242
243typedef enum {
244 SV_HANDLER_TRY = 1,
245 SV_HANDLER_FINALLY = 2,
246} sv_handler_kind_t;
247
248typedef struct {
249 uint8_t *ip;
250 int saved_sp;
251 uint8_t kind;
252} sv_handler_t;
253
254struct sv_upvalue {
255 ant_value_t *location;
256 ant_value_t closed;
257 struct sv_upvalue *next;
258 uint64_t gc_epoch;
259};
260
261static inline sv_upvalue_t *js_upvalue_alloc(void) {
262 return (sv_upvalue_t *)fixed_arena_alloc(&rt->js->upvalue_arena);
263}
264
265bool sv_slot_has_open_upvalue(sv_vm_t *vm, ant_value_t *slot);
266
267#define SV_CALL_HAS_BOUND_ARGS (1u << 0)
268#define SV_CALL_HAS_SUPER (1u << 1)
269#define SV_CALL_IS_ARROW (1u << 2)
270#define SV_CALL_IS_DEFAULT_CTOR (1u << 3)
271#define SV_CALL_BORROWED_UPVALS (1u << 4)
272
273typedef struct sv_closure {
274 uint32_t call_flags;
275 int bound_argc;
276 sv_func_t *func;
277
278 sv_upvalue_t **upvalues;
279 ant_value_t bound_this;
280 ant_value_t *bound_argv;
281 ant_value_t bound_args;
282 ant_value_t super_val;
283 ant_value_t func_obj;
284
285 uint64_t gc_epoch;
286} sv_closure_t;
287
288static inline sv_closure_t *js_closure_alloc(ant_t *js) {
289 sv_closure_t *c = (sv_closure_t *)fixed_arena_alloc(&js->closure_arena);
290 if (c) c->gc_epoch = gc_get_epoch();
291 return c;
292}
293
294static inline sv_closure_t *js_func_closure(ant_value_t func) {
295 return (sv_closure_t *)(uintptr_t)vdata(func);
296}
297
298static inline ant_value_t js_func_obj(ant_value_t func) {
299 return js_func_closure(func)->func_obj;
300}
301
302static inline ant_value_t js_as_obj(ant_value_t v) {
303 uint8_t t = vtype(v);
304 if (t == T_OBJ) return v;
305 if (t == T_FUNC) return js_func_obj(v);
306 return mkval(T_OBJ, vdata(v));
307}
308
309ant_value_t sv_execute_closure_entry(
310 sv_vm_t *vm,sv_closure_t *closure,
311 ant_value_t callee_func, ant_value_t super_val,
312 ant_value_t this_val, ant_value_t *args,
313 int argc, ant_value_t *out_this
314);
315
316#ifdef ANT_JIT
317typedef struct {
318 bool active;
319 int bc_offset;
320 ant_value_t *locals;
321 int n_locals;
322 ant_value_t *lp;
323} sv_jit_osr_t;
324#endif
325
326#define SV_TRY_MAX 64
327#define SV_TDZ T_EMPTY
328#define SV_HANDLER_MAX (SV_TRY_MAX * 2)
329
330#define SV_FRAMES_HARD_MAX 65536
331#define SV_STACK_HARD_MAX 524288
332
333struct sv_vm {
334 ant_t *js;
335
336 ant_value_t *stack;
337 int sp;
338 int stack_size;
339
340 sv_frame_t *frames;
341 int fp;
342 int max_frames;
343
344 sv_handler_t handler_stack[SV_HANDLER_MAX];
345 sv_upvalue_t *open_upvalues;
346 int handler_depth;
347
348 // TODO: move to nested struct
349 bool suspended;
350 bool suspended_resume_pending;
351 bool suspended_resume_is_error;
352 sv_resume_kind_t suspended_resume_kind;
353
354 int suspended_entry_fp;
355 int suspended_saved_fp;
356 ant_value_t suspended_resume_value;
357
358#ifdef ANT_JIT
359 struct {
360 bool active;
361 int64_t ip_offset;
362 ant_value_t *locals;
363 int64_t n_locals;
364 ant_value_t *vstack;
365 int64_t vstack_sp;
366 } jit_resume;
367
368 sv_jit_osr_t jit_osr;
369#endif
370};
371
372static inline uint8_t sv_get_u8(const uint8_t *ip) { return ip[0]; }
373static inline int8_t sv_get_i8(const uint8_t *ip) { return (int8_t)ip[0]; }
374
375static inline uint16_t sv_get_u16(const uint8_t *ip) {
376 uint16_t v; memcpy(&v, ip, 2); return v;
377}
378
379static inline int16_t sv_get_i16(const uint8_t *ip) {
380 int16_t v; memcpy(&v, ip, 2); return v;
381}
382
383static inline uint32_t sv_get_u32(const uint8_t *ip) {
384 uint32_t v; memcpy(&v, ip, 4); return v;
385}
386
387static inline int32_t sv_get_i32(const uint8_t *ip) {
388 int32_t v; memcpy(&v, ip, 4); return v;
389}
390
391static inline const char *sv_atom_cstr(sv_atom_t *a, char *buf, size_t bufsz) {
392 size_t n = a->len < bufsz - 1 ? a->len : bufsz - 1;
393 memcpy(buf, a->str, n);
394 buf[n] = '\0';
395 return buf;
396}
397
398static inline bool sv_frame_is_strict(const sv_frame_t *frame) {
399 return frame && frame->func && frame->func->is_strict;
400}
401
402static inline bool sv_slot_in_range(
403 const ant_value_t *base, size_t count,
404 const ant_value_t *slot
405) {
406 if (!base || !slot || count == 0) return false;
407
408 uintptr_t lo = (uintptr_t)base;
409 uintptr_t hi = lo + count * sizeof(*base);
410 uintptr_t addr = (uintptr_t)slot;
411 return addr >= lo && addr < hi;
412}
413
414static inline bool sv_slot_in_vm_stack(const sv_vm_t *vm, const ant_value_t *slot) {
415 return vm && sv_slot_in_range(vm->stack, (size_t)vm->stack_size, slot);
416}
417
418static inline bool sv_is_nullish_this(ant_value_t v) {
419 return
420 vtype(v) == T_UNDEF || vtype(v) == T_NULL ||
421 (vtype(v) == T_OBJ && vdata(v) == 0);
422}
423
424static inline ant_value_t sv_normalize_this_for_frame(ant_t *js, sv_func_t *func, ant_value_t this_val) {
425 if (!func || func->is_arrow) return this_val;
426 if (func->is_strict) return sv_is_nullish_this(this_val) ? js_mkundef() : this_val;
427 return sv_is_nullish_this(this_val) ? js->global : this_val;
428}
429
430static inline bool sv_vm_is_strict(const sv_vm_t *vm) {
431 if (vm && vm->fp >= 0) {
432 const sv_frame_t *f = &vm->frames[vm->fp];
433 return f->func && f->func->is_strict;
434 }
435 return false;
436}
437
438static inline sv_vm_t *sv_vm_get_active(ant_t *js) {
439 if (!js) return NULL;
440 if (js->active_async_coro) {
441 if (js->active_async_coro->sv_vm) return js->active_async_coro->sv_vm;
442 if (js->active_async_coro->owner_vm) return js->active_async_coro->owner_vm;
443 }
444 return js->vm;
445}
446
447static inline bool sv_is_strict_context(ant_t *js) {
448 return sv_vm_is_strict(sv_vm_get_active(js));
449}
450
451static inline ant_value_t sv_vm_get_new_target(const sv_vm_t *vm, ant_t *js) {
452 if (vm && vm->fp >= 0) return vm->frames[vm->fp].new_target;
453 return js->new_target;
454}
455
456static inline ant_value_t sv_vm_get_super_val(const sv_vm_t *vm) {
457 if (vm && vm->fp >= 0) return vm->frames[vm->fp].super_val;
458 return js_mkundef();
459}
460
461static inline int sv_frame_arg_slots(const sv_frame_t *frame) {
462 if (!frame || !frame->func) return 0;
463 return frame->argc > frame->func->param_count ? frame->argc : frame->func->param_count;
464}
465
466static inline ant_value_t sv_frame_get_arg_value(const sv_frame_t *frame, uint16_t idx) {
467 int arg_slots = sv_frame_arg_slots(frame);
468 if (!frame || !frame->bp || (int)idx >= arg_slots) return js_mkundef();
469 return frame->bp[idx];
470}
471
472static inline void sv_frame_set_arg_value(ant_t *js, sv_frame_t *frame, uint16_t idx, ant_value_t val) {
473 int arg_slots = sv_frame_arg_slots(frame);
474 if (!frame || !frame->bp || (int)idx >= arg_slots) return;
475 frame->bp[idx] = val;
476 if (vtype(frame->arguments_obj) != T_UNDEF)
477 js_arguments_sync_slot(js, frame->arguments_obj, idx, val);
478}
479
480static inline ant_value_t *sv_frame_slot_ptr(sv_frame_t *frame, uint16_t slot_idx) {
481 if (!frame || !frame->func) return NULL;
482 int param_count = frame->func->param_count;
483 if ((int)slot_idx < param_count) {
484 int arg_slots = sv_frame_arg_slots(frame);
485 if ((int)slot_idx >= arg_slots || !frame->bp) return NULL;
486 return &frame->bp[slot_idx];
487 }
488 if (!frame->lp) return NULL;
489 return &frame->lp[slot_idx - param_count];
490}
491
492static inline uint16_t sv_frame_total_slots(const sv_frame_t *frame) {
493 if (!frame || !frame->func) return 0;
494 int total = frame->func->param_count + frame->func->max_locals;
495 return total > 0 ? (uint16_t)total : 0;
496}
497
498static inline void sv_vm_maybe_checkpoint_microtasks(ant_t *js) {
499 if (!js || js->microtasks_draining || js->vm_exec_depth != 0) return;
500 js_maybe_drain_microtasks(js);
501}
502
503ant_value_t sv_string_builder_read_value(
504 ant_t *js, ant_value_t value
505);
506
507ant_value_t sv_string_builder_flush_slot(
508 sv_vm_t *vm, ant_t *js,
509 sv_frame_t *frame, uint16_t slot_idx
510);
511
512ant_value_t sv_string_builder_append_slot(
513 sv_vm_t *vm, ant_t *js, sv_frame_t *frame,
514 sv_func_t *func, uint16_t slot_idx, ant_value_t rhs
515);
516
517ant_value_t sv_string_builder_append_snapshot_slot(
518 sv_vm_t *vm, ant_t *js, sv_frame_t *frame,
519 sv_func_t *func, uint16_t slot_idx, ant_value_t lhs, ant_value_t rhs
520);
521
522typedef struct {
523 ant_value_t this_val;
524 ant_value_t super_val;
525 ant_value_t *args;
526 int argc;
527 ant_value_t *alloc;
528} sv_call_ctx_t;
529
530typedef enum {
531 SV_CALL_MODE_NORMAL = 0,
532 SV_CALL_MODE_EXPLICIT_THIS,
533 SV_CALL_MODE_CONSTRUCT,
534} sv_call_mode_t;
535
536typedef enum {
537 SV_CALL_EXEC_NATIVE = 0,
538 SV_CALL_EXEC_PROXY_APPLY,
539 SV_CALL_EXEC_PROXY_CONSTRUCT,
540 SV_CALL_EXEC_DEFAULT_CTOR,
541 SV_CALL_EXEC_CLOSURE,
542} sv_call_exec_kind_t;
543
544typedef struct {
545 sv_call_exec_kind_t kind;
546 ant_value_t func;
547 sv_closure_t *closure;
548 sv_call_ctx_t ctx;
549} sv_call_plan_t;
550
551static inline ant_value_t *sv_prepend_bound_args(
552 sv_closure_t *closure, ant_value_t *args, int argc, int *out_total
553) {
554 int total = closure->bound_argc + argc;
555 ant_value_t *combined = malloc(sizeof(ant_value_t) * (size_t)total);
556
557 if (!combined) { *out_total = argc; return NULL; }
558 memcpy(combined, closure->bound_argv, sizeof(ant_value_t) * (size_t)closure->bound_argc);
559 memcpy(combined + closure->bound_argc, args, sizeof(ant_value_t) * (size_t)argc);
560
561 *out_total = total;
562 return combined;
563}
564
565static inline bool sv_call_mode_is_construct(sv_call_mode_t mode) {
566 return mode == SV_CALL_MODE_CONSTRUCT;
567}
568
569static inline ant_value_t sv_call_normalize_this(ant_t *js, ant_value_t this_val, sv_call_mode_t mode) {
570 if (mode == SV_CALL_MODE_NORMAL && sv_is_nullish_this(this_val)) return js->global;
571 return this_val;
572}
573
574static inline ant_value_t sv_call_resolve_bound(
575 ant_t *js, sv_closure_t *closure,
576 sv_call_ctx_t *ctx, sv_call_mode_t mode
577) {
578 uint32_t flags = closure->call_flags;
579
580 if (flags & SV_CALL_IS_ARROW) ctx->this_val = closure->bound_this;
581 else if (!sv_call_mode_is_construct(mode) && vtype(closure->bound_this) != T_UNDEF)
582 ctx->this_val = closure->bound_this;
583
584 if ((flags & SV_CALL_HAS_BOUND_ARGS) && closure->bound_argc > 0) {
585 int total;
586 ant_value_t *combined = sv_prepend_bound_args(closure, ctx->args, ctx->argc, &total);
587 if (!combined) return js_mkerr(js, "out of memory");
588 ctx->args = combined;
589 ctx->argc = total;
590 ctx->alloc = combined;
591 }
592
593 if (flags & SV_CALL_HAS_SUPER) ctx->super_val = closure->super_val;
594 return js_mkundef();
595}
596
597static inline void sv_call_cleanup(ant_t *js, sv_call_ctx_t *ctx) {
598 if (ctx->alloc) { free(ctx->alloc); ctx->alloc = NULL; }
599}
600
601static inline ant_value_t sv_call_default_ctor(
602 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
603 sv_call_ctx_t *ctx, ant_value_t *out_this
604);
605
606static inline ant_value_t sv_call_resolve_closure(
607 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
608 ant_value_t callee_func, sv_call_ctx_t *ctx, ant_value_t *out_this
609);
610
611static inline ant_value_t sv_prepare_call(
612 sv_vm_t *vm, ant_t *js, ant_value_t func,
613 ant_value_t this_val, ant_value_t *args, int argc,
614 ant_value_t *out_this, sv_call_mode_t mode, sv_call_plan_t *plan
615) {
616 bool is_construct_call = sv_call_mode_is_construct(mode);
617
618 plan->kind = SV_CALL_EXEC_NATIVE;
619 plan->func = func;
620 plan->closure = NULL;
621
622 plan->ctx = (sv_call_ctx_t){
623 .this_val = this_val,
624 .super_val = js_mkundef(),
625 .args = args,
626 .argc = argc,
627 .alloc = NULL,
628 };
629
630 if (!is_construct_call) js->new_target = js_mkundef();
631 if (out_this) *out_this = this_val;
632
633 if (is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) {
634 plan->kind = SV_CALL_EXEC_PROXY_CONSTRUCT;
635 return js_mkundef();
636 }
637
638 if (is_construct_call && !js_is_constructor(func))
639 return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor");
640
641 if (!is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) {
642 plan->kind = SV_CALL_EXEC_PROXY_APPLY;
643 return js_mkundef();
644 }
645
646 if (vtype(func) == T_CFUNC) {
647 plan->ctx.this_val = sv_call_normalize_this(js, this_val, mode);
648 if (out_this) *out_this = plan->ctx.this_val;
649 return js_mkundef();
650 }
651
652 if (vtype(func) != T_FUNC)
653 return js_mkerr_typed(js, JS_ERR_TYPE, "%s is not a function", typestr(vtype(func)));
654
655 sv_closure_t *closure = js_func_closure(func);
656 plan->closure = closure;
657
658 ant_value_t err = sv_call_resolve_bound(js, closure, &plan->ctx, mode);
659 if (is_err(err)) return err;
660
661 if (is_construct_call) plan->ctx.this_val = this_val;
662 if (out_this) *out_this = plan->ctx.this_val;
663
664 if (closure->call_flags & SV_CALL_IS_DEFAULT_CTOR) {
665 plan->kind = SV_CALL_EXEC_DEFAULT_CTOR;
666 return js_mkundef();
667 }
668
669 if (closure->func != NULL) {
670 plan->kind = SV_CALL_EXEC_CLOSURE;
671 return js_mkundef();
672 }
673
674 return js_mkundef();
675}
676
677static inline ant_value_t sv_execute_call_plan(
678 sv_vm_t *vm, ant_t *js, sv_call_plan_t *plan, ant_value_t *out_this
679) {
680 switch (plan->kind) {
681 case SV_CALL_EXEC_PROXY_APPLY: return js_proxy_apply(
682 js, plan->func, plan->ctx.this_val, plan->ctx.args, plan->ctx.argc
683 );
684
685 case SV_CALL_EXEC_PROXY_CONSTRUCT: return js_proxy_construct(
686 js, plan->func, plan->ctx.args, plan->ctx.argc, sv_vm_get_new_target(vm, js)
687 );
688
689 case SV_CALL_EXEC_DEFAULT_CTOR: return sv_call_default_ctor(
690 vm, js, plan->closure, &plan->ctx, out_this
691 );
692
693 case SV_CALL_EXEC_CLOSURE: return sv_call_resolve_closure(
694 vm, js, plan->closure, plan->func, &plan->ctx, out_this
695 );
696
697 case SV_CALL_EXEC_NATIVE: {
698 ant_value_t result = sv_call_native(
699 js, plan->func, plan->ctx.this_val, plan->ctx.args, plan->ctx.argc
700 );
701 sv_call_cleanup(js, &plan->ctx);
702 return result;
703 }}
704
705 return js_mkerr(js, "invalid call plan");
706}
707
708static inline bool sv_check_c_stack_overflow(ant_t *js) {
709 volatile char marker;
710 if (js->cstk.limit == 0 || js->cstk.base == NULL) return false;
711
712 uintptr_t base = (uintptr_t)js->cstk.base;
713 uintptr_t curr = (uintptr_t)▮
714
715 size_t used = (base > curr) ? (base - curr) : (curr - base);
716 return used > js->cstk.limit;
717}
718
719static inline ant_value_t sv_vm_call(
720 sv_vm_t *vm, ant_t *js, ant_value_t func,
721 ant_value_t this_val, ant_value_t *args, int argc,
722 ant_value_t *out_this, bool is_construct_call
723) {
724 if (sv_check_c_stack_overflow(js))
725 return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum call stack size exceeded");
726
727 sv_call_mode_t mode = is_construct_call
728 ? SV_CALL_MODE_CONSTRUCT
729 : SV_CALL_MODE_NORMAL;
730
731 sv_call_plan_t plan;
732 ant_value_t err = sv_prepare_call(
733 vm, js, func, this_val, args, argc,
734 out_this, mode, &plan
735 );
736
737 if (is_err(err)) return err;
738 ant_value_t result = sv_execute_call_plan(vm, js, &plan, out_this);
739 sv_vm_maybe_checkpoint_microtasks(js);
740
741 return result;
742}
743
744static inline ant_value_t sv_vm_call_explicit_this(
745 sv_vm_t *vm, ant_t *js, ant_value_t func,
746 ant_value_t this_val, ant_value_t *args, int argc
747) {
748 if (sv_check_c_stack_overflow(js))
749 return js_mkerr_typed(js, JS_ERR_RANGE | JS_ERR_NO_STACK, "Maximum call stack size exceeded");
750
751 sv_call_plan_t plan;
752 ant_value_t err = sv_prepare_call(
753 vm, js, func, this_val, args, argc, NULL,
754 SV_CALL_MODE_EXPLICIT_THIS, &plan
755 );
756
757 if (is_err(err)) return err;
758 ant_value_t result = sv_execute_call_plan(vm, js, &plan, NULL);
759 sv_vm_maybe_checkpoint_microtasks(js);
760
761 return result;
762}
763
764static inline ant_value_t sv_call_default_ctor(
765 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
766 sv_call_ctx_t *ctx, ant_value_t *out_this
767) {
768 if (vtype(js->new_target) == T_UNDEF) {
769 sv_call_cleanup(js, ctx);
770 return js_mkerr_typed(
771 js, JS_ERR_TYPE,
772 "Class constructor cannot be invoked without 'new'"
773 );}
774
775 ant_value_t super_ctor = closure->super_val;
776 uint8_t st = vtype(super_ctor);
777
778 if (st == T_FUNC || st == T_CFUNC) {
779 ant_value_t super_this = ctx->this_val;
780 ant_value_t result = sv_vm_call(
781 vm, js, super_ctor, ctx->this_val,
782 ctx->args, ctx->argc, &super_this, true
783 );
784
785 if (out_this) *out_this = super_this;
786 sv_call_cleanup(js, ctx);
787
788 return result;
789 }
790
791 sv_call_cleanup(js, ctx);
792 return js_mkundef();
793}
794
795ant_value_t sv_call_async_closure_dispatch(
796 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
797 ant_value_t callee_func, ant_value_t super_val,
798 ant_value_t this_val, ant_value_t *args, int argc
799);
800
801ant_value_t sv_call_generator_closure_dispatch(
802 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
803 ant_value_t callee_func, ant_value_t super_val,
804 ant_value_t this_val, ant_value_t *args, int argc
805);
806
807static inline ant_value_t sv_call_async_closure(
808 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
809 ant_value_t callee_func, sv_call_ctx_t *ctx
810) {
811 ant_value_t result = sv_call_async_closure_dispatch(
812 vm, js, closure, callee_func,
813 ctx->super_val, ctx->this_val, ctx->args, ctx->argc
814 );
815 sv_call_cleanup(js, ctx);
816 return result;
817}
818
819static inline ant_value_t sv_call_generator_closure(
820 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
821 ant_value_t callee_func, sv_call_ctx_t *ctx
822) {
823 ant_value_t result = sv_call_generator_closure_dispatch(
824 vm, js, closure, callee_func,
825 ctx->super_val, ctx->this_val, ctx->args, ctx->argc
826 );
827 sv_call_cleanup(js, ctx);
828 return result;
829}
830
831static inline ant_value_t sv_call_closure(
832 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
833 ant_value_t callee_func, sv_call_ctx_t *ctx, ant_value_t *out_this
834) {
835 ant_value_t result = sv_execute_closure_entry(
836 vm, closure, callee_func, ctx->super_val,
837 ctx->this_val, ctx->args, ctx->argc, out_this
838 );
839 sv_call_cleanup(js, ctx);
840 return result;
841}
842
843#ifdef ANT_JIT
844
845#define SV_TFB_NUM (1 << 0)
846#define SV_TFB_STR (1 << 1)
847#define SV_TFB_BOOL (1 << 2)
848#define SV_TFB_OTHER (1 << 3)
849#define SV_TFB_CTOR_PROP_BINS 17
850#define SV_TFB_CTOR_PROP_OVERFLOW_FROM (SV_TFB_CTOR_PROP_BINS - 1)
851#define SV_TFB_INOBJ_SLACK_ALLOCATIONS 32
852#define SV_TFB_INOBJ_P90_NUMERATOR 9
853#define SV_TFB_INOBJ_P90_DENOMINATOR 10
854
855#define SV_JIT_THRESHOLD 100
856#define SV_JIT_RECOMPILE_DELAY 50
857#define SV_TFB_ALLOC_THRESHOLD 2
858
859#define SV_CALL_FB_MAX_SLOTS 32
860#define SV_CALL_FB_MISS_DISABLE 4
861
862#define SV_JIT_RETRY_INTERP mkval(T_ERR, 1)
863#define SV_JIT_MAGIC 0xBA110ULL
864
865#define SV_JIT_BAILOUT \
866 (NANBOX_PREFIX \
867 | ((ant_value_t)T_SENTINEL << NANBOX_TYPE_SHIFT) \
868 | SV_JIT_MAGIC)
869
870static inline bool sv_is_jit_bailout(ant_value_t v) {
871 return v == SV_JIT_BAILOUT;
872}
873
874static inline void sv_jit_enter(ant_t *js) {
875 if (js) js->jit_active_depth++;
876}
877
878static inline void sv_jit_leave(ant_t *js) {
879 if (js && js->jit_active_depth > 0) js->jit_active_depth--;
880}
881
882static inline void sv_jit_on_bailout(sv_func_t *fn) {
883 fn->jit_code = NULL;
884 fn->back_edge_count = 0;
885 fn->call_count = SV_JIT_THRESHOLD - SV_JIT_RECOMPILE_DELAY;
886}
887
888typedef ant_value_t (*sv_jit_func_t)(
889 sv_vm_t *,
890 ant_value_t,
891 ant_value_t,
892 ant_value_t,
893 ant_value_t *,
894 int, sv_closure_t *
895);
896
897ant_value_t sv_jit_try_compile_and_call(sv_vm_t *vm, ant_t *js,
898 sv_closure_t *closure, ant_value_t callee_func,
899 sv_call_ctx_t *ctx, ant_value_t *out_this
900);
901
902static inline uint8_t sv_tfb_classify(ant_value_t v) {
903 if (vtype(v) == T_NUM) return SV_TFB_NUM;
904 if (vtype(v) == T_STR) return SV_TFB_STR;
905 if (vtype(v) == T_BOOL) return SV_TFB_BOOL;
906 return SV_TFB_OTHER;
907}
908
909static inline void sv_tfb_record2(sv_func_t *func, uint8_t *ip, ant_value_t l, ant_value_t r) {
910if (func->type_feedback) {
911 int off = (int)(ip - func->code);
912 uint8_t old = func->type_feedback[off];
913 uint8_t neu = old | sv_tfb_classify(l) | sv_tfb_classify(r);
914 if (neu != old) { func->type_feedback[off] = neu; func->tfb_version++; }
915}}
916
917static inline void sv_tfb_record1(sv_func_t *func, uint8_t *ip, ant_value_t v) {
918if (func->type_feedback) {
919 int off = (int)(ip - func->code);
920 uint8_t old = func->type_feedback[off];
921 uint8_t neu = old | sv_tfb_classify(v);
922 if (neu != old) { func->type_feedback[off] = neu; func->tfb_version++; }
923}}
924
925static inline void sv_tfb_seed_param_hints(sv_func_t *fn) {
926 if (!fn->param_type_feedback || !fn->param_hints) return;
927 int n = fn->param_hint_count < fn->param_count
928 ? fn->param_hint_count : fn->param_count;
929 for (int i = 0; i < n; i++) {
930 if (fn->param_hints[i].type == SV_TI_NUM)
931 fn->param_type_feedback[i] |= SV_TFB_NUM;
932 }
933}
934
935static inline void sv_tfb_ensure(sv_func_t *fn) {
936 if (!fn->type_feedback && fn->code_len > 0)
937 fn->type_feedback = calloc((size_t)fn->code_len, 1);
938 if (!fn->local_type_feedback && fn->max_locals > 0)
939 fn->local_type_feedback = calloc((size_t)fn->max_locals, 1);
940 if (!fn->param_type_feedback && fn->param_count > 0) {
941 fn->param_type_feedback = calloc((size_t)fn->param_count, 1);
942 sv_tfb_seed_param_hints(fn);
943 }
944}
945
946static inline void sv_tfb_record_param(sv_func_t *func, int idx, ant_value_t v) {
947 if (func->param_type_feedback && idx >= 0 && idx < func->param_count) {
948 uint8_t old = func->param_type_feedback[idx];
949 uint8_t neu = old | sv_tfb_classify(v);
950 if (neu != old) { func->param_type_feedback[idx] = neu; func->tfb_version++; }
951 }
952}
953
954static inline bool sv_tfb_param_numeric_hint(const sv_func_t *func, int idx) {
955 if (!func || idx < 0 || idx >= func->param_count) return false;
956 uint8_t fb = func->param_type_feedback ? func->param_type_feedback[idx] : 0;
957 if (fb && (fb & ~SV_TFB_NUM)) return false;
958 if (fb == SV_TFB_NUM) return true;
959 return (
960 func->param_hints &&
961 idx < func->param_hint_count &&
962 func->param_hints[idx].type == SV_TI_NUM
963 );
964}
965
966static inline void sv_tfb_record_call_target(sv_func_t *func, int bc_off, sv_func_t *callee) {
967 if (!callee) return;
968 sv_call_target_fb_t *fb = func->call_target_fb;
969 int count = func->call_target_fb_count;
970 for (int i = 0; i < count; i++) {
971 if (fb[i].bc_off != (uint16_t)bc_off) continue;
972 if (fb[i].disabled) return;
973 if (fb[i].target == callee) return;
974 if (fb[i].target == NULL) { fb[i].target = callee; return; }
975 fb[i].miss_count++;
976 if (fb[i].miss_count >= SV_CALL_FB_MISS_DISABLE) {
977 fb[i].disabled = 1;
978 fb[i].target = NULL;
979 } else {
980 fb[i].target = callee;
981 }
982 func->tfb_version++;
983 return;
984 }
985 if (count >= SV_CALL_FB_MAX_SLOTS) return;
986 if (!fb) {
987 fb = calloc(SV_CALL_FB_MAX_SLOTS, sizeof(sv_call_target_fb_t));
988 if (!fb) return;
989 func->call_target_fb = fb;
990 }
991 fb[count].bc_off = (uint16_t)bc_off;
992 fb[count].target = callee;
993 fb[count].miss_count = 0;
994 fb[count].disabled = 0;
995 func->call_target_fb_count = (uint8_t)(count + 1);
996}
997
998static inline sv_func_t *sv_tfb_get_call_target(sv_func_t *func, int bc_off) {
999 sv_call_target_fb_t *fb = func->call_target_fb;
1000 int count = func->call_target_fb_count;
1001 for (int i = 0; i < count; i++) {
1002 if (fb[i].bc_off == (uint16_t)bc_off && !fb[i].disabled)
1003 return fb[i].target;
1004 }
1005 return NULL;
1006}
1007
1008static inline void sv_tfb_record_local(sv_func_t *func, int idx, ant_value_t v) {
1009 if (func->local_type_feedback && idx >= 0 && idx < func->max_locals) {
1010 uint8_t old = func->local_type_feedback[idx];
1011 uint8_t neu = old | sv_tfb_classify(v);
1012 if (neu != old) { func->local_type_feedback[idx] = neu; func->tfb_version++; }
1013 }
1014}
1015
1016static inline uint8_t sv_tfb_clamp_inobj_limit(uint32_t limit) {
1017 return (limit > ANT_INOBJ_MAX_SLOTS) ? (uint8_t)ANT_INOBJ_MAX_SLOTS : (uint8_t)limit;
1018}
1019
1020static inline uint8_t sv_tfb_infer_inobj_limit(const sv_func_t *func, uint64_t samples) {
1021 if (!func || samples == 0) return (uint8_t)ANT_INOBJ_MAX_SLOTS;
1022
1023 uint64_t target = (
1024 (samples * SV_TFB_INOBJ_P90_NUMERATOR)
1025 + (SV_TFB_INOBJ_P90_DENOMINATOR - 1)
1026 ) / SV_TFB_INOBJ_P90_DENOMINATOR;
1027 if (target == 0) target = 1;
1028
1029 uint64_t seen = 0;
1030 for (uint32_t i = 0; i < SV_TFB_CTOR_PROP_BINS; i++) {
1031 seen += func->ctor_prop_hist[i];
1032 if (seen < target) continue;
1033
1034 if (i >= SV_TFB_CTOR_PROP_OVERFLOW_FROM) return (uint8_t)ANT_INOBJ_MAX_SLOTS;
1035 return sv_tfb_clamp_inobj_limit(i);
1036 }
1037
1038 return (uint8_t)ANT_INOBJ_MAX_SLOTS;
1039}
1040
1041static inline void sv_tfb_record_ctor_prop_count(ant_value_t ctor_func, ant_value_t instance) {
1042 if (vtype(ctor_func) != T_FUNC) return;
1043 if (!is_object_type(instance)) return;
1044 sv_closure_t *closure = js_func_closure(ctor_func);
1045 if (!closure || !closure->func) return;
1046 ant_object_t *obj = js_obj_ptr(js_as_obj(instance));
1047 if (!obj) return;
1048
1049 sv_func_t *func = closure->func;
1050 uint32_t count = obj->prop_count;
1051 uint32_t bin = (count < SV_TFB_CTOR_PROP_OVERFLOW_FROM)
1052 ? count
1053 : SV_TFB_CTOR_PROP_OVERFLOW_FROM;
1054 func->ctor_prop_hist[bin]++;
1055 uint64_t samples = ++func->ctor_prop_samples;
1056 if (!func->ctor_inobj_frozen && samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) {
1057 func->ctor_inobj_limit = sv_tfb_infer_inobj_limit(func, samples);
1058 func->ctor_inobj_frozen = 1;
1059 }
1060}
1061
1062static inline uint8_t sv_tfb_ctor_inobj_limit(ant_value_t ctor_func) {
1063 if (vtype(ctor_func) != T_FUNC) return (uint8_t)ANT_INOBJ_MAX_SLOTS;
1064 sv_closure_t *closure = js_func_closure(ctor_func);
1065 if (!closure || !closure->func) return (uint8_t)ANT_INOBJ_MAX_SLOTS;
1066
1067 sv_func_t *func = closure->func;
1068 if (!func->ctor_inobj_frozen) return (uint8_t)ANT_INOBJ_MAX_SLOTS;
1069 return sv_tfb_clamp_inobj_limit(func->ctor_inobj_limit);
1070}
1071
1072static inline bool sv_tfb_ctor_inobj_limit_frozen(ant_value_t ctor_func) {
1073 if (vtype(ctor_func) != T_FUNC) return false;
1074 sv_closure_t *closure = js_func_closure(ctor_func);
1075 if (!closure || !closure->func) return false;
1076 return closure->func->ctor_inobj_frozen != 0;
1077}
1078
1079static inline uint32_t sv_tfb_ctor_inobj_slack_remaining(ant_value_t ctor_func) {
1080 if (vtype(ctor_func) != T_FUNC) return SV_TFB_INOBJ_SLACK_ALLOCATIONS;
1081 sv_closure_t *closure = js_func_closure(ctor_func);
1082 if (!closure || !closure->func) return SV_TFB_INOBJ_SLACK_ALLOCATIONS;
1083 sv_func_t *func = closure->func;
1084 if (func->ctor_inobj_frozen || func->ctor_prop_samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) return 0;
1085 return (uint32_t)(SV_TFB_INOBJ_SLACK_ALLOCATIONS - func->ctor_prop_samples);
1086}
1087#endif
1088
1089#ifndef ANT_JIT
1090static inline void sv_tfb_record_ctor_prop_count(ant_value_t ctor_func, ant_value_t instance) {
1091 (void)ctor_func;
1092 (void)instance;
1093}
1094
1095static inline uint8_t sv_tfb_ctor_inobj_limit(ant_value_t ctor_func) {
1096 (void)ctor_func;
1097 return (uint8_t)ANT_INOBJ_MAX_SLOTS;
1098}
1099
1100static inline bool sv_tfb_ctor_inobj_limit_frozen(ant_value_t ctor_func) {
1101 (void)ctor_func;
1102 return false;
1103}
1104
1105static inline uint32_t sv_tfb_ctor_inobj_slack_remaining(ant_value_t ctor_func) {
1106 (void)ctor_func;
1107 return 0;
1108}
1109#endif
1110
1111static inline ant_value_t sv_call_resolve_closure(
1112 sv_vm_t *vm, ant_t *js, sv_closure_t *closure,
1113 ant_value_t callee_func, sv_call_ctx_t *ctx, ant_value_t *out_this
1114) {
1115 if (closure->func->is_generator)
1116 return sv_call_generator_closure(vm, js, closure, callee_func, ctx);
1117 if (closure->func->is_async)
1118 return sv_call_async_closure(vm, js, closure, callee_func, ctx);
1119#ifdef ANT_JIT
1120 if (!closure->func->is_generator) {
1121 sv_func_t *fn = closure->func;
1122 if (fn->jit_code) {
1123 sv_jit_enter(js);
1124 ant_value_t result = ((sv_jit_func_t)fn->jit_code)(
1125 vm, ctx->this_val, js->new_target,
1126 ctx->super_val, ctx->args, ctx->argc, closure
1127 );
1128 sv_jit_leave(js);
1129 if (sv_is_jit_bailout(result)) {
1130 sv_jit_on_bailout(fn);
1131 } else { sv_call_cleanup(js, ctx); return result; }
1132 }
1133 {
1134 uint32_t cc = ++fn->call_count;
1135 if (__builtin_expect(cc == SV_TFB_ALLOC_THRESHOLD, 0))
1136 sv_tfb_ensure(fn);
1137 if (cc > SV_JIT_THRESHOLD) {
1138 ant_value_t result = sv_jit_try_compile_and_call(vm, js, closure, callee_func, ctx, out_this);
1139 if (result != SV_JIT_RETRY_INTERP) return result;
1140 }
1141 }
1142 }
1143#endif
1144 return sv_call_closure(vm, js, closure, callee_func, ctx, out_this);
1145}
1146
1147#endif