This commit is contained in:
2026-02-03 16:59:20 -06:00
parent a171a0d2af
commit 03c45ee8b0

View File

@@ -82,7 +82,7 @@
*/
// #define DUMP_BYTECODE (1)
/* dump GC summary: old/new heap, recovery %, heap growth */
#define DUMP_GC
// #define DUMP_GC
/* dump detailed GC: roots, scanning, object traversal (implies DUMP_GC) */
// #define DUMP_GC_DETAIL
#ifdef DUMP_GC_DETAIL
@@ -97,7 +97,7 @@
// #define DUMP_ROPE_REBALANCE
/* test the GC by forcing it before each object allocation */
//#define FORCE_GC_AT_MALLOC
#define FORCE_GC_AT_MALLOC
#define POISON_HEAP
/* POISON_HEAP: Use ASan's memory poisoning to detect stale pointer access */
@@ -856,6 +856,9 @@ struct JSContext {
JSInterruptHandler *interrupt_handler;
void *interrupt_opaque;
/* Parser state (for GC to scan cpool during parsing) */
struct JSFunctionDef *current_parse_fd;
};
/* ============================================================
@@ -1864,6 +1867,7 @@ static JSValue js_cell_number_max (JSContext *ctx, JSValue this_val, int argc, J
static JSValue js_cell_number_remainder (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_object (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
JSValue JS_ThrowOutOfMemory (JSContext *ctx);
static JSVarRef *get_var_ref (JSContext *ctx, JSStackFrame *sf, int var_idx, BOOL is_arg);
static JSValue JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *input, size_t input_len, const char *filename, int flags, int scope_idx);
@@ -1958,6 +1962,50 @@ void js_free (JSContext *ctx, void *ptr) {
(void)ptr;
}
/* Parser memory functions - use system allocator to avoid GC issues */
static void *pjs_malloc (size_t size) {
return malloc (size);
}
static void *pjs_mallocz (size_t size) {
void *ptr = malloc (size);
if (ptr) memset (ptr, 0, size);
return ptr;
}
static void *pjs_realloc (void *ptr, size_t size) {
return realloc (ptr, size);
}
static void pjs_free (void *ptr) {
free (ptr);
}
/* Parser-specific resize array using system allocator */
static no_inline int pjs_realloc_array (void **parray, int elem_size, int *psize, int req_size) {
int new_size;
void *new_array;
size_t old_size;
new_size = max_int (req_size, *psize * 3 / 2);
old_size = (size_t)(*psize) * elem_size;
new_array = pjs_realloc (*parray, (size_t)new_size * elem_size);
if (!new_array) return -1;
/* Zero newly allocated memory */
if (new_size > *psize) {
memset ((char *)new_array + old_size, 0, (size_t)(new_size - *psize) * elem_size);
}
*psize = new_size;
*parray = new_array;
return 0;
}
static inline int pjs_resize_array (void **parray, int elem_size, int *psize, int req_size) {
if (unlikely (req_size > *psize))
return pjs_realloc_array (parray, elem_size, psize, req_size);
else
return 0;
}
static no_inline int js_realloc_array (JSContext *ctx, void **parray, int elem_size, int *psize, int req_size) {
int new_size;
void *new_array;
@@ -2422,7 +2470,19 @@ static void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8
JSFunction *fn = (JSFunction *)ptr;
/* Scan the function name */
fn->name = gc_copy_value (ctx, fn->name, from_base, from_end, to_base, to_free, to_end);
/* Note: function_bytecode and var_refs are allocated via js_malloc, not bump allocator */
/* Scan bytecode's cpool - it contains JSValues that may reference GC objects */
if (fn->kind == JS_FUNC_KIND_BYTECODE && fn->u.func.function_bytecode) {
JSFunctionBytecode *b = fn->u.func.function_bytecode;
/* Scan cpool entries */
for (int i = 0; i < b->cpool_count; i++) {
b->cpool[i] = gc_copy_value (ctx, b->cpool[i], from_base, from_end, to_base, to_free, to_end);
}
/* Scan func_name and filename */
b->func_name = gc_copy_value (ctx, b->func_name, from_base, from_end, to_base, to_free, to_end);
if (b->has_debug) {
b->debug.filename = gc_copy_value (ctx, b->debug.filename, from_base, from_end, to_base, to_free, to_end);
}
}
break;
}
case OBJ_TEXT:
@@ -2446,6 +2506,34 @@ static void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8
}
}
/* Forward declaration - defined after JSFunctionDef */
static void gc_scan_parser_cpool (JSContext *ctx, uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end);
/* Scan OBJ_CODE cpool for bytecode objects outside GC heap */
static void gc_scan_bytecode_cpool (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end) {
if (!JS_IsPtr (v)) return;
void *ptr = JS_VALUE_GET_PTR (v);
if (ptr_in_range (ptr, from_base, from_end)) return; /* On GC heap, handled normally */
if (is_stone_ptr (ctx, ptr)) return; /* Stone memory */
/* Check if this is an OBJ_CODE outside GC heap */
objhdr_t hdr = *(objhdr_t *)ptr;
if (objhdr_type (hdr) == OBJ_CODE) {
JSFunctionBytecode *bc = (JSFunctionBytecode *)ptr;
/* Scan cpool entries */
for (int i = 0; i < bc->cpool_count; i++) {
bc->cpool[i] = gc_copy_value (ctx, bc->cpool[i], from_base, from_end, to_base, to_free, to_end);
}
/* Scan func_name and filename */
bc->func_name = gc_copy_value (ctx, bc->func_name, from_base, from_end, to_base, to_free, to_end);
if (bc->has_debug) {
bc->debug.filename = gc_copy_value (ctx, bc->debug.filename, from_base, from_end, to_base, to_free, to_end);
}
}
}
/* Cheney copying GC - collect garbage and compact live objects
allow_grow: if true, grow heap when <20% recovered; if false, keep same size */
static int ctx_gc (JSContext *ctx, int allow_grow) {
@@ -2509,6 +2597,12 @@ static int ctx_gc (JSContext *ctx, int allow_grow) {
#endif
ctx->eval_obj = gc_copy_value (ctx, ctx->eval_obj, from_base, from_end, to_base, &to_free, to_end);
/* Copy current exception if pending */
#ifdef DUMP_GC_DETAIL
printf(" roots: current_exception\n"); fflush(stdout);
#endif
rt->current_exception = gc_copy_value (ctx, rt->current_exception, from_base, from_end, to_base, &to_free, to_end);
#ifdef DUMP_GC_DETAIL
printf(" roots: native_error_proto\n"); fflush(stdout);
#endif
@@ -2542,11 +2636,24 @@ static int ctx_gc (JSContext *ctx, int allow_grow) {
frame->this_obj = gc_copy_value (ctx, frame->this_obj, from_base, from_end, to_base, &to_free, to_end);
}
/* Copy JSStackFrame chain (C stack frames) */
#ifdef DUMP_GC_DETAIL
printf(" roots: current_stack_frame chain\n"); fflush(stdout);
#endif
for (JSStackFrame *sf = rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
sf->cur_func = gc_copy_value (ctx, sf->cur_func, from_base, from_end, to_base, &to_free, to_end);
/* Also scan bytecode cpool if it's a bytecode object outside GC heap */
gc_scan_bytecode_cpool (ctx, sf->cur_func, from_base, from_end, to_base, &to_free, to_end);
/* Scan arg_buf and var_buf - they point into value_stack which is scanned separately,
but we should update cur_func which may be a function/bytecode */
}
/* Copy JS_PUSH_VALUE/JS_POP_VALUE roots */
#ifdef DUMP_GC_DETAIL
printf(" roots: top_gc_ref\n"); fflush(stdout);
#endif
for (JSGCRef *ref = ctx->top_gc_ref; ref != NULL; ref = ref->prev) {
gc_scan_bytecode_cpool (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
ref->val = gc_copy_value (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
}
@@ -2555,9 +2662,15 @@ static int ctx_gc (JSContext *ctx, int allow_grow) {
printf(" roots: last_gc_ref\n"); fflush(stdout);
#endif
for (JSGCRef *ref = ctx->last_gc_ref; ref != NULL; ref = ref->prev) {
gc_scan_bytecode_cpool (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
ref->val = gc_copy_value (ctx, ref->val, from_base, from_end, to_base, &to_free, to_end);
}
/* Scan parser's cpool (if parsing is in progress) */
if (ctx->current_parse_fd) {
gc_scan_parser_cpool (ctx, from_base, from_end, to_base, &to_free, to_end);
}
/* Cheney scan: scan copied objects to find more references */
uint8_t *scan = to_base;
#ifdef DUMP_GC_DETAIL
@@ -4095,26 +4208,35 @@ static void build_backtrace (JSContext *ctx, JSValue error_obj, const char *file
const char *func_name_str;
const char *str1;
JSRecord *p;
JSGCRef err_ref;
if (!JS_IsObject (error_obj))
return; /* protection in the out of memory case */
/* Protect error_obj from GC during backtrace building */
JS_PushGCRef (ctx, &err_ref);
err_ref.val = error_obj;
js_dbuf_init (ctx, &dbuf);
if (filename) {
dbuf_printf (&dbuf, " at %s", filename);
if (line_num != -1) dbuf_printf (&dbuf, ":%d:%d", line_num, col_num);
dbuf_putc (&dbuf, '\n');
str = JS_NewString (ctx, filename);
if (JS_IsException (str)) return;
if (JS_IsException (str)) {
JS_PopGCRef (ctx, &err_ref);
return;
}
/* Note: SpiderMonkey does that, could update once there is a standard */
JSValue key_fileName = JS_KEY_STR (ctx, "fileName");
JSValue key_lineNumber = JS_KEY_STR (ctx, "lineNumber");
JSValue key_columnNumber = JS_KEY_STR (ctx, "columnNumber");
if (JS_SetPropertyInternal (ctx, error_obj, key_fileName, str) < 0
|| JS_SetPropertyInternal (ctx, error_obj, key_lineNumber, JS_NewInt32 (ctx, line_num))
if (JS_SetPropertyInternal (ctx, err_ref.val, key_fileName, str) < 0
|| JS_SetPropertyInternal (ctx, err_ref.val, key_lineNumber, JS_NewInt32 (ctx, line_num))
< 0
|| JS_SetPropertyInternal (ctx, error_obj, key_columnNumber, JS_NewInt32 (ctx, col_num))
|| JS_SetPropertyInternal (ctx, err_ref.val, key_columnNumber, JS_NewInt32 (ctx, col_num))
< 0) {
JS_PopGCRef (ctx, &err_ref);
return;
}
}
@@ -4163,7 +4285,8 @@ static void build_backtrace (JSContext *ctx, JSValue error_obj, const char *file
else
str = JS_NewString (ctx, (char *)dbuf.buf);
dbuf_free (&dbuf);
JS_SetPropertyInternal (ctx, error_obj, JS_KEY_stack, str);
JS_SetPropertyInternal (ctx, err_ref.val, JS_KEY_stack, str);
JS_PopGCRef (ctx, &err_ref);
}
/* Note: it is important that no exception is returned by this function */
@@ -6618,12 +6741,20 @@ static JSValue js_closure (JSContext *ctx, JSValue bfunc, JSVarRef **cur_var_ref
JSFunctionBytecode *b;
JSValue func_obj;
JSFunction *f;
JSGCRef bfunc_ref;
/* Protect bfunc from GC during function allocation */
JS_PUSH_VALUE (ctx, bfunc);
b = JS_VALUE_GET_PTR (bfunc);
func_obj = js_new_function (ctx, JS_FUNC_KIND_BYTECODE);
if (JS_IsException (func_obj)) {
JS_POP_VALUE (ctx, bfunc);
return JS_EXCEPTION;
}
JS_POP_VALUE (ctx, bfunc);
b = JS_VALUE_GET_PTR (bfunc);
func_obj = js_closure2 (ctx, func_obj, b, cur_var_refs, sf);
if (JS_IsException (func_obj)) {
/* bfunc has been freed */
@@ -6853,8 +6984,8 @@ static JSValue JS_CallInternal (JSContext *caller_ctx, JSValue func_obj, JSValue
return JS_ThrowTypeError (caller_ctx, "not a function");
}
f = JS_VALUE_GET_FUNCTION (func_obj);
/* Strict arity enforcement: too many arguments throws */
if (unlikely (argc > f->length)) {
/* Strict arity enforcement: too many arguments throws (length < 0 means variadic) */
if (unlikely (f->length >= 0 && argc > f->length)) {
char buf[KEY_GET_STR_BUF_SIZE];
return JS_ThrowTypeError (
caller_ctx, "too many arguments for %s: expected %d, got %d", JS_KeyGetStr (caller_ctx, buf, KEY_GET_STR_BUF_SIZE, f->name), f->length, argc);
@@ -7167,7 +7298,6 @@ restart:
ret_val = JS_CallInternal (ctx, call_argv[-1], JS_NULL, call_argc, call_argv, 0);
if (unlikely (JS_IsException (ret_val))) goto exception;
if (opcode == OP_tail_call) goto done;
for (i = -1; i < call_argc; i++)
sp -= call_argc + 1;
*sp++ = ret_val;
}
@@ -9115,6 +9245,33 @@ typedef struct JSFunctionDef {
int source_len;
} JSFunctionDef;
/* Scan a single JSFunctionDef's cpool and recursively scan its children */
static void gc_scan_parser_fd (JSContext *ctx, JSFunctionDef *fd,
uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end) {
if (!fd) return;
/* Scan constant pool */
for (int i = 0; i < fd->cpool_count; i++) {
fd->cpool[i] = gc_copy_value (ctx, fd->cpool[i], from_base, from_end, to_base, to_free, to_end);
}
/* Scan func_name */
fd->func_name = gc_copy_value (ctx, fd->func_name, from_base, from_end, to_base, to_free, to_end);
/* Scan filename */
fd->filename = gc_copy_value (ctx, fd->filename, from_base, from_end, to_base, to_free, to_end);
/* Recursively scan child functions */
struct list_head *el;
list_for_each (el, &fd->child_list) {
JSFunctionDef *child = list_entry (el, JSFunctionDef, link);
gc_scan_parser_fd (ctx, child, from_base, from_end, to_base, to_free, to_end);
}
}
/* Scan parser's cpool values during GC - called when parsing is in progress */
static void gc_scan_parser_cpool (JSContext *ctx, uint8_t *from_base, uint8_t *from_end,
uint8_t *to_base, uint8_t **to_free, uint8_t *to_end) {
gc_scan_parser_fd (ctx, ctx->current_parse_fd, from_base, from_end, to_base, to_free, to_end);
}
typedef struct JSToken {
int val;
const uint8_t *ptr; /* position in the source */
@@ -10854,7 +11011,7 @@ static int new_label_fd (JSFunctionDef *fd) {
int label;
LabelSlot *ls;
if (js_resize_array (fd->ctx, (void *)&fd->label_slots, sizeof (fd->label_slots[0]), &fd->label_size, fd->label_count + 1))
if (pjs_resize_array ((void **)&fd->label_slots, sizeof (fd->label_slots[0]), &fd->label_size, fd->label_count + 1))
return -1;
label = fd->label_count++;
ls = &fd->label_slots[label];
@@ -10909,7 +11066,8 @@ static int emit_goto (JSParseState *s, int opcode, int label) {
/* return the constant pool index. 'val' is not duplicated. */
static int fd_cpool_add (JSContext *ctx, JSFunctionDef *fd, JSValue val) {
if (js_resize_array (ctx, (void *)&fd->cpool, sizeof (fd->cpool[0]), &fd->cpool_size, fd->cpool_count + 1))
(void)ctx;
if (pjs_resize_array ((void **)&fd->cpool, sizeof (fd->cpool[0]), &fd->cpool_size, fd->cpool_count + 1))
return -1;
fd->cpool[fd->cpool_count++] = val;
return fd->cpool_count - 1;
@@ -11015,11 +11173,11 @@ static int push_scope (JSParseState *s) {
/* XXX: potential arithmetic overflow */
new_size = max_int (fd->scope_count + 1, fd->scope_size * 3 / 2);
if (fd->scopes == fd->def_scope_array) {
new_buf = js_realloc (s->ctx, NULL, new_size * sizeof (*fd->scopes));
new_buf = pjs_malloc (new_size * sizeof (*fd->scopes));
if (!new_buf) return -1;
memcpy (new_buf, fd->scopes, fd->scope_count * sizeof (*fd->scopes));
} else {
new_buf = js_realloc (s->ctx, fd->scopes, new_size * sizeof (*fd->scopes));
new_buf = pjs_realloc (fd->scopes, new_size * sizeof (*fd->scopes));
if (!new_buf) return -1;
}
fd->scopes = new_buf;
@@ -11073,7 +11231,7 @@ static int add_var (JSContext *ctx, JSFunctionDef *fd, JSValue name) {
JS_ThrowInternalError (ctx, "too many local variables");
return -1;
}
if (js_resize_array (ctx, (void **)&fd->vars, sizeof (fd->vars[0]), &fd->var_size, fd->var_count + 1))
if (pjs_resize_array ((void **)&fd->vars, sizeof (fd->vars[0]), &fd->var_size, fd->var_count + 1))
return -1;
vd = &fd->vars[fd->var_count++];
memset (vd, 0, sizeof (*vd));
@@ -11113,7 +11271,7 @@ static int add_arg (JSContext *ctx, JSFunctionDef *fd, JSValue name) {
JS_ThrowInternalError (ctx, "too many arguments");
return -1;
}
if (js_resize_array (ctx, (void **)&fd->args, sizeof (fd->args[0]), &fd->arg_size, fd->arg_count + 1))
if (pjs_resize_array ((void **)&fd->args, sizeof (fd->args[0]), &fd->arg_size, fd->arg_count + 1))
return -1;
vd = &fd->args[fd->arg_count++];
memset (vd, 0, sizeof (*vd));
@@ -11125,8 +11283,9 @@ static int add_arg (JSContext *ctx, JSFunctionDef *fd, JSValue name) {
/* add a global variable definition */
static JSGlobalVar *add_global_var (JSContext *ctx, JSFunctionDef *s, JSValue name) {
JSGlobalVar *hf;
(void)ctx;
if (js_resize_array (ctx, (void **)&s->global_vars, sizeof (s->global_vars[0]), &s->global_var_size, s->global_var_count + 1))
if (pjs_resize_array ((void **)&s->global_vars, sizeof (s->global_vars[0]), &s->global_var_size, s->global_var_count + 1))
return NULL;
hf = &s->global_vars[s->global_var_count++];
hf->cpool_idx = -1;
@@ -13936,7 +14095,7 @@ static JSFunctionDef *
js_new_function_def (JSContext *ctx, JSFunctionDef *parent, BOOL is_eval, BOOL is_func_expr, const char *filename, const uint8_t *source_ptr, GetLineColCache *get_line_col_cache) {
JSFunctionDef *fd;
fd = js_mallocz (ctx, sizeof (*fd));
fd = pjs_mallocz (sizeof (*fd));
if (!fd) return NULL;
fd->ctx = ctx;
@@ -14027,42 +14186,43 @@ static void js_free_function_def (JSContext *ctx, JSFunctionDef *fd) {
free_bytecode_atoms (ctx->rt, fd->byte_code.buf, fd->byte_code.size, fd->use_short_opcodes);
dbuf_free (&fd->byte_code);
js_free (ctx, fd->jump_slots);
js_free (ctx, fd->label_slots);
js_free (ctx, fd->line_number_slots);
pjs_free (fd->jump_slots);
pjs_free (fd->label_slots);
pjs_free (fd->line_number_slots);
for (i = 0; i < fd->cpool_count; i++) {
}
js_free (ctx, fd->cpool);
pjs_free (fd->cpool);
for (i = 0; i < fd->var_count; i++) {
}
js_free (ctx, fd->vars);
pjs_free (fd->vars);
for (i = 0; i < fd->arg_count; i++) {
}
js_free (ctx, fd->args);
pjs_free (fd->args);
for (i = 0; i < fd->global_var_count; i++) {
}
js_free (ctx, fd->global_vars);
pjs_free (fd->global_vars);
for (i = 0; i < fd->closure_var_count; i++) {
JSClosureVar *cv = &fd->closure_var[i];
(void)cv;
}
js_free (ctx, fd->closure_var);
pjs_free (fd->closure_var);
if (fd->scopes != fd->def_scope_array) js_free (ctx, fd->scopes);
if (fd->scopes != fd->def_scope_array) pjs_free (fd->scopes);
dbuf_free (&fd->pc2line);
js_free (ctx, fd->source);
pjs_free (fd->source);
if (fd->parent) {
/* remove in parent list */
list_del (&fd->link);
}
js_free (ctx, fd);
pjs_free (fd);
}
#ifdef DUMP_BYTECODE
@@ -14461,7 +14621,7 @@ static int add_closure_var (JSContext *ctx, JSFunctionDef *s, BOOL is_local, BOO
return -1;
}
if (js_resize_array (ctx, (void **)&s->closure_var, sizeof (s->closure_var[0]), &s->closure_var_size, s->closure_var_count + 1))
if (pjs_resize_array ((void **)&s->closure_var, sizeof (s->closure_var[0]), &s->closure_var_size, s->closure_var_count + 1))
return -1;
cv = &s->closure_var[s->closure_var_count++];
cv->is_local = is_local;
@@ -16092,13 +16252,13 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
#if SHORT_OPCODES
if (s->jump_size) {
s->jump_slots
= js_mallocz (s->ctx, sizeof (*s->jump_slots) * s->jump_size);
= pjs_mallocz (sizeof (*s->jump_slots) * s->jump_size);
if (s->jump_slots == NULL) return -1;
}
#endif
/* XXX: Should skip this phase if not generating SHORT_OPCODES */
if (s->line_number_size && !s->strip_debug) {
s->line_number_slots = js_mallocz (s->ctx, sizeof (*s->line_number_slots) * s->line_number_size);
s->line_number_slots = pjs_mallocz (sizeof (*s->line_number_slots) * s->line_number_size);
if (s->line_number_slots == NULL) return -1;
s->line_number_last = s->source_pos;
s->line_number_last_pc = 0;
@@ -16725,14 +16885,14 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
}
}
}
js_free (ctx, s->jump_slots);
pjs_free (s->jump_slots);
s->jump_slots = NULL;
#endif
js_free (ctx, s->label_slots);
pjs_free (s->label_slots);
s->label_slots = NULL;
/* XXX: should delay until copying to runtime bytecode function */
compute_pc2line_info (s);
js_free (ctx, s->line_number_slots);
pjs_free (s->line_number_slots);
s->line_number_slots = NULL;
/* set the new byte code */
dbuf_free (&s->byte_code);
@@ -16792,7 +16952,7 @@ static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int
s->catch_pos_tab[pos] = catch_pos;
/* queue the new PC to explore */
if (js_resize_array (ctx, (void **)&s->pc_stack, sizeof (s->pc_stack[0]), &s->pc_stack_size, s->pc_stack_len + 1))
if (pjs_resize_array ((void **)&s->pc_stack, sizeof (s->pc_stack[0]), &s->pc_stack_size, s->pc_stack_len + 1))
return -1;
s->pc_stack[s->pc_stack_len++] = pos;
return 0;
@@ -16808,12 +16968,12 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
s->bc_len = fd->byte_code.size;
/* bc_len > 0 */
s->stack_level_tab
= js_malloc (ctx, sizeof (s->stack_level_tab[0]) * s->bc_len);
= pjs_malloc (sizeof (s->stack_level_tab[0]) * s->bc_len);
if (!s->stack_level_tab) return -1;
for (i = 0; i < s->bc_len; i++)
s->stack_level_tab[i] = 0xffff;
s->pc_stack = NULL;
s->catch_pos_tab = js_malloc (ctx, sizeof (s->catch_pos_tab[0]) * s->bc_len);
s->catch_pos_tab = pjs_malloc (sizeof (s->catch_pos_tab[0]) * s->bc_len);
if (!s->catch_pos_tab) goto fail;
s->stack_len_max = 0;
@@ -16942,15 +17102,15 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
if (ss_check (ctx, s, pos_next, op, stack_len, catch_pos)) goto fail;
done_insn:;
}
js_free (ctx, s->pc_stack);
js_free (ctx, s->catch_pos_tab);
js_free (ctx, s->stack_level_tab);
pjs_free (s->pc_stack);
pjs_free (s->catch_pos_tab);
pjs_free (s->stack_level_tab);
*pstack_size = s->stack_len_max;
return 0;
fail:
js_free (ctx, s->pc_stack);
js_free (ctx, s->catch_pos_tab);
js_free (ctx, s->stack_level_tab);
pjs_free (s->pc_stack);
pjs_free (s->catch_pos_tab);
pjs_free (s->stack_level_tab);
*pstack_size = 0;
return -1;
}
@@ -17049,7 +17209,8 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
byte_code_offset = function_size;
function_size += fd->byte_code.size;
b = js_mallocz (ctx, function_size);
/* Bytecode is immutable - allocate outside GC heap */
b = pjs_mallocz (function_size);
if (!b) goto fail;
b->header = objhdr_make (0, OBJ_CODE, false, false, false, false);
@@ -17104,20 +17265,21 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
// DynBuf pc2line;
// compute_pc2line_info(fd, &pc2line);
// js_free(ctx, fd->line_number_slots)
b->debug.pc2line_buf = js_realloc (ctx, fd->pc2line.buf, fd->pc2line.size);
if (!b->debug.pc2line_buf) b->debug.pc2line_buf = fd->pc2line.buf;
/* pc2line.buf is allocated by dbuf (system realloc), so just use it directly */
b->debug.pc2line_buf = fd->pc2line.buf;
fd->pc2line.buf = NULL; /* Transfer ownership */
b->debug.pc2line_len = fd->pc2line.size;
b->debug.source = fd->source;
b->debug.source_len = fd->source_len;
}
if (fd->scopes != fd->def_scope_array) js_free (ctx, fd->scopes);
if (fd->scopes != fd->def_scope_array) pjs_free (fd->scopes);
b->closure_var_count = fd->closure_var_count;
if (b->closure_var_count) {
b->closure_var = (void *)((uint8_t *)b + closure_var_offset);
memcpy (b->closure_var, fd->closure_var, b->closure_var_count * sizeof (*b->closure_var));
}
js_free (ctx, fd->closure_var);
pjs_free (fd->closure_var);
fd->closure_var = NULL;
b->has_prototype = fd->has_prototype;
@@ -17740,6 +17902,7 @@ static JSValue __JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *
fd = js_new_function_def (ctx, NULL, TRUE, FALSE, filename, s->buf_start, &s->get_line_col_cache);
if (!fd) goto fail1;
s->cur_func = fd;
ctx->current_parse_fd = fd; /* Set GC root for parser's cpool */
fd->eval_type = eval_type;
fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
fd->js_mode = js_mode;
@@ -17755,11 +17918,13 @@ static JSValue __JS_EvalInternal (JSContext *ctx, JSValue this_obj, const char *
if (err) {
fail:
free_token (s, &s->token);
ctx->current_parse_fd = NULL; /* Clear GC root before freeing */
js_free_function_def (ctx, fd);
goto fail1;
}
/* create the function object and all the enclosed functions */
ctx->current_parse_fd = NULL; /* Clear GC root - fd ownership transfers to js_create_function */
fun_obj = js_create_function (ctx, fd);
if (JS_IsException (fun_obj)) goto fail1;
/* Could add a flag to avoid resolution if necessary */
@@ -22544,6 +22709,20 @@ static JSValue js_cell_text_format (JSContext *ctx, JSValue this_val, int argc,
return pretext_end (ctx, result);
}
/* print(args...) - print arguments to stdout */
static JSValue js_print (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
for (int i = 0; i < argc; i++) {
const char *str = JS_ToCString (ctx, argv[i]);
if (str) {
fputs (str, stdout);
JS_FreeCString (ctx, str);
}
if (i < argc - 1) fputc (' ', stdout);
}
fputc ('\n', stdout);
return JS_NULL;
}
/* ----------------------------------------------------------------------------
* array function and sub-functions
* ----------------------------------------------------------------------------
@@ -25377,6 +25556,9 @@ void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
js_set_global_cfunc(ctx, "push", js_cell_push, 2);
js_set_global_cfunc(ctx, "pop", js_cell_pop, 1);
js_set_global_cfunc(ctx, "meme", js_cell_meme, 2);
/* I/O functions */
js_set_global_cfunc(ctx, "print", js_print, -1); /* variadic: length < 0 means no arg limit */
}
}