Compare commits
3 Commits
simplify_c
...
cellfix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
970386a04b | ||
|
|
75ddb1fb4f | ||
|
|
bc9d8a338a |
326
source/quickjs.c
326
source/quickjs.c
@@ -8521,12 +8521,10 @@ restart:
|
|||||||
int expr_count = get_u16 (pc); pc += 2;
|
int expr_count = get_u16 (pc); pc += 2;
|
||||||
uint32_t cpool_idx = get_u32 (pc); pc += 4;
|
uint32_t cpool_idx = get_u32 (pc); pc += 4;
|
||||||
|
|
||||||
/* Expression values are on the stack. We'll process them in place,
|
/* Use PPretext (system malloc) to avoid GC issues during string building */
|
||||||
building the result string using pretext. */
|
PPretext *result = ppretext_init (64);
|
||||||
JSText *result = pretext_init (ctx, 64);
|
|
||||||
if (!result) goto exception;
|
if (!result) goto exception;
|
||||||
|
|
||||||
/* Re-read format_str after pretext_init (may have triggered GC) */
|
|
||||||
JSValue format_str = b->cpool[cpool_idx];
|
JSValue format_str = b->cpool[cpool_idx];
|
||||||
|
|
||||||
/* Parse format string and substitute {N} with stringified stack values.
|
/* Parse format string and substitute {N} with stringified stack values.
|
||||||
@@ -8537,9 +8535,6 @@ restart:
|
|||||||
int pos = 0;
|
int pos = 0;
|
||||||
|
|
||||||
while (pos < fmt_len) {
|
while (pos < fmt_len) {
|
||||||
/* Re-read format_str in case GC moved the cpool entry */
|
|
||||||
format_str = b->cpool[cpool_idx];
|
|
||||||
|
|
||||||
/* Find next '{' */
|
/* Find next '{' */
|
||||||
int brace_start = -1;
|
int brace_start = -1;
|
||||||
for (int i = pos; i < fmt_len; i++) {
|
for (int i = pos; i < fmt_len; i++) {
|
||||||
@@ -8551,23 +8546,20 @@ restart:
|
|||||||
|
|
||||||
if (brace_start < 0) {
|
if (brace_start < 0) {
|
||||||
/* No more braces, copy rest of string */
|
/* No more braces, copy rest of string */
|
||||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
|
||||||
for (int i = pos; i < fmt_len; i++) {
|
for (int i = pos; i < fmt_len; i++) {
|
||||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
result = ppretext_putc (result, js_string_value_get (format_str, i));
|
||||||
if (!result) goto exception;
|
if (!result) goto exception;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy text before brace */
|
/* Copy text before brace */
|
||||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
|
||||||
for (int i = pos; i < brace_start; i++) {
|
for (int i = pos; i < brace_start; i++) {
|
||||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
result = ppretext_putc (result, js_string_value_get (format_str, i));
|
||||||
if (!result) goto exception;
|
if (!result) goto exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find closing '}' */
|
/* Find closing '}' */
|
||||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
|
||||||
int brace_end = -1;
|
int brace_end = -1;
|
||||||
for (int i = brace_start + 1; i < fmt_len; i++) {
|
for (int i = brace_start + 1; i < fmt_len; i++) {
|
||||||
if (js_string_value_get (format_str, i) == '}') {
|
if (js_string_value_get (format_str, i) == '}') {
|
||||||
@@ -8578,7 +8570,7 @@ restart:
|
|||||||
|
|
||||||
if (brace_end < 0) {
|
if (brace_end < 0) {
|
||||||
/* No closing brace, copy '{' and continue */
|
/* No closing brace, copy '{' and continue */
|
||||||
result = pretext_putc (ctx, result, '{');
|
result = ppretext_putc (result, '{');
|
||||||
if (!result) goto exception;
|
if (!result) goto exception;
|
||||||
pos = brace_start + 1;
|
pos = brace_start + 1;
|
||||||
continue;
|
continue;
|
||||||
@@ -8586,7 +8578,6 @@ restart:
|
|||||||
|
|
||||||
/* Parse index from {N} */
|
/* Parse index from {N} */
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
|
||||||
for (int i = brace_start + 1; i < brace_end; i++) {
|
for (int i = brace_start + 1; i < brace_end; i++) {
|
||||||
uint32_t c = js_string_value_get (format_str, i);
|
uint32_t c = js_string_value_get (format_str, i);
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
@@ -8602,19 +8593,21 @@ restart:
|
|||||||
JSValue val = expr_base[idx];
|
JSValue val = expr_base[idx];
|
||||||
JSValue str_val = JS_ToString (ctx, val);
|
JSValue str_val = JS_ToString (ctx, val);
|
||||||
if (JS_IsException (str_val)) {
|
if (JS_IsException (str_val)) {
|
||||||
|
ppretext_free (result);
|
||||||
goto exception;
|
goto exception;
|
||||||
}
|
}
|
||||||
|
/* Re-read format_str after JS_ToString (may have triggered GC) */
|
||||||
|
format_str = b->cpool[cpool_idx];
|
||||||
/* Append stringified value to result */
|
/* Append stringified value to result */
|
||||||
int str_len = js_string_value_len (str_val);
|
int str_len = js_string_value_len (str_val);
|
||||||
for (int i = 0; i < str_len; i++) {
|
for (int i = 0; i < str_len; i++) {
|
||||||
result = pretext_putc (ctx, result, js_string_value_get (str_val, i));
|
result = ppretext_putc (result, js_string_value_get (str_val, i));
|
||||||
if (!result) goto exception;
|
if (!result) goto exception;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Invalid index, keep original {N} */
|
/* Invalid index, keep original {N} */
|
||||||
format_str = b->cpool[cpool_idx]; /* Re-read */
|
|
||||||
for (int i = brace_start; i <= brace_end; i++) {
|
for (int i = brace_start; i <= brace_end; i++) {
|
||||||
result = pretext_putc (ctx, result, js_string_value_get (format_str, i));
|
result = ppretext_putc (result, js_string_value_get (format_str, i));
|
||||||
if (!result) goto exception;
|
if (!result) goto exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8622,8 +8615,8 @@ restart:
|
|||||||
pos = brace_end + 1;
|
pos = brace_end + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Finalize result string */
|
/* Finalize result string - this is the only GC point */
|
||||||
JSValue result_str = pretext_end (ctx, result);
|
JSValue result_str = ppretext_end (ctx, result);
|
||||||
if (JS_IsException (result_str)) goto exception;
|
if (JS_IsException (result_str)) goto exception;
|
||||||
|
|
||||||
/* Pop expr values, push result */
|
/* Pop expr values, push result */
|
||||||
@@ -11943,6 +11936,14 @@ static __exception int get_lvalue (JSParseState *s, int *popcode, int *pscope, J
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* remove the last opcode */
|
/* remove the last opcode */
|
||||||
|
{
|
||||||
|
const char *fn = JS_ToCString(s->ctx, fd->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: get_lvalue truncating bc from %zu to %d (opcode=%d)\n",
|
||||||
|
fd->byte_code.size, fd->last_opcode_pos, opcode);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(s->ctx, fn);
|
||||||
|
}
|
||||||
fd->byte_code.size = fd->last_opcode_pos;
|
fd->byte_code.size = fd->last_opcode_pos;
|
||||||
fd->last_opcode_pos = -1;
|
fd->last_opcode_pos = -1;
|
||||||
|
|
||||||
@@ -12069,7 +12070,23 @@ static void put_lvalue (JSParseState *s, int opcode, int scope, JSValue name, in
|
|||||||
emit_prop_key (s, name); /* name has refcount */
|
emit_prop_key (s, name); /* name has refcount */
|
||||||
break;
|
break;
|
||||||
case OP_get_array_el:
|
case OP_get_array_el:
|
||||||
|
{
|
||||||
|
const char *fn = JS_ToCString(s->ctx, s->cur_func->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: emitting put_array_el at bc position %zu\n", s->cur_func->byte_code.size);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(s->ctx, fn);
|
||||||
|
}
|
||||||
emit_op (s, OP_put_array_el);
|
emit_op (s, OP_put_array_el);
|
||||||
|
{
|
||||||
|
const char *fn = JS_ToCString(s->ctx, s->cur_func->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: after emit, bc size=%zu, last byte=0x%02x\n",
|
||||||
|
s->cur_func->byte_code.size,
|
||||||
|
s->cur_func->byte_code.buf[s->cur_func->byte_code.size - 1]);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(s->ctx, fn);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
abort ();
|
abort ();
|
||||||
@@ -13274,6 +13291,14 @@ static void emit_return (JSParseState *s, BOOL hasval) {
|
|||||||
top = top->prev;
|
top = top->prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const char *fn = JS_ToCString(s->ctx, s->cur_func->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: emitting return at bc position %zu, hasval=%d\n",
|
||||||
|
s->cur_func->byte_code.size, hasval);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(s->ctx, fn);
|
||||||
|
}
|
||||||
emit_op (s, hasval ? OP_return : OP_return_undef);
|
emit_op (s, hasval ? OP_return : OP_return_undef);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15296,10 +15321,28 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
line_num = 0; /* avoid warning */
|
line_num = 0; /* avoid warning */
|
||||||
|
|
||||||
|
/* Debug: check if this is create_actor */
|
||||||
|
int is_create_actor = 0;
|
||||||
|
if (!JS_IsNull(s->func_name)) {
|
||||||
|
const char *fn = JS_ToCString(ctx, s->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
is_create_actor = 1;
|
||||||
|
printf("DEBUG resolve_variables: processing create_actor, bc_len=%d\n", bc_len);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(ctx, fn);
|
||||||
|
}
|
||||||
|
|
||||||
for (pos = 0; pos < bc_len; pos = pos_next) {
|
for (pos = 0; pos < bc_len; pos = pos_next) {
|
||||||
op = bc_buf[pos];
|
op = bc_buf[pos];
|
||||||
len = opcode_info[op].size;
|
len = opcode_info[op].size;
|
||||||
pos_next = pos + len;
|
pos_next = pos + len;
|
||||||
|
|
||||||
|
if (is_create_actor) {
|
||||||
|
printf("DEBUG: pos=%d op=0x%02x len=%d bc_out.size=%zu\n",
|
||||||
|
pos, op, len, bc_out.size);
|
||||||
|
}
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case OP_line_num:
|
case OP_line_num:
|
||||||
line_num = get_u32 (bc_buf + pos + 1);
|
line_num = get_u32 (bc_buf + pos + 1);
|
||||||
@@ -15387,8 +15430,12 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
/* remove dead code */
|
/* remove dead code */
|
||||||
int line = -1;
|
int line = -1;
|
||||||
dbuf_put (&bc_out, bc_buf + pos, len);
|
dbuf_put (&bc_out, bc_buf + pos, len);
|
||||||
|
int old_pos = pos + len;
|
||||||
pos = skip_dead_code (s, bc_buf, bc_len, pos + len, &line);
|
pos = skip_dead_code (s, bc_buf, bc_len, pos + len, &line);
|
||||||
pos_next = pos;
|
pos_next = pos;
|
||||||
|
if (is_create_actor && pos != old_pos) {
|
||||||
|
printf("DEBUG: skip_dead_code skipped from %d to %d (skipped %d bytes)\n", old_pos, pos, pos - old_pos);
|
||||||
|
}
|
||||||
if (pos < bc_len && line >= 0 && line_num != line) {
|
if (pos < bc_len && line >= 0 && line_num != line) {
|
||||||
line_num = line;
|
line_num = line;
|
||||||
s->line_number_size++;
|
s->line_number_size++;
|
||||||
@@ -15515,6 +15562,10 @@ static __exception int resolve_variables (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_create_actor) {
|
||||||
|
printf("DEBUG: resolve_variables finished. bc_out.size=%zu (was bc_len=%d)\n", bc_out.size, bc_len);
|
||||||
|
}
|
||||||
|
|
||||||
/* set the new byte code */
|
/* set the new byte code */
|
||||||
dbuf_free (&s->byte_code);
|
dbuf_free (&s->byte_code);
|
||||||
s->byte_code = bc_out;
|
s->byte_code = bc_out;
|
||||||
@@ -15778,6 +15829,21 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
cc.bc_len = bc_len = s->byte_code.size;
|
cc.bc_len = bc_len = s->byte_code.size;
|
||||||
js_dbuf_init (ctx, &bc_out);
|
js_dbuf_init (ctx, &bc_out);
|
||||||
|
|
||||||
|
/* Debug: dump original bytecode for create_actor */
|
||||||
|
if (!JS_IsNull(s->func_name)) {
|
||||||
|
const char *fn = JS_ToCString(ctx, s->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("=== ORIGINAL bytecode for %s ===\n", fn);
|
||||||
|
printf("bc_len=%d\n", bc_len);
|
||||||
|
for (int di = 0; di < bc_len; di++) {
|
||||||
|
printf("%02x ", bc_buf[di]);
|
||||||
|
if ((di + 1) % 16 == 0) printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(ctx, fn);
|
||||||
|
}
|
||||||
|
|
||||||
#if SHORT_OPCODES
|
#if SHORT_OPCODES
|
||||||
if (s->jump_size) {
|
if (s->jump_size) {
|
||||||
s->jump_slots
|
s->jump_slots
|
||||||
@@ -16337,6 +16403,10 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
assert (label_slots[i].first_reloc == NULL);
|
assert (label_slots[i].first_reloc == NULL);
|
||||||
}
|
}
|
||||||
#if SHORT_OPCODES
|
#if SHORT_OPCODES
|
||||||
|
/* NOTE: Jump shrink optimization disabled due to bytecode corruption bug.
|
||||||
|
The memmove-based shrinking was not correctly updating all position
|
||||||
|
references, leading to invalid opcodes (0 bytes) in the output. */
|
||||||
|
#if 0
|
||||||
if (OPTIMIZE) {
|
if (OPTIMIZE) {
|
||||||
/* more jump optimizations */
|
/* more jump optimizations */
|
||||||
int patch_offsets = 0;
|
int patch_offsets = 0;
|
||||||
@@ -16410,6 +16480,7 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
pjs_free (s->jump_slots);
|
pjs_free (s->jump_slots);
|
||||||
s->jump_slots = NULL;
|
s->jump_slots = NULL;
|
||||||
#endif
|
#endif
|
||||||
@@ -16427,6 +16498,20 @@ static __exception int resolve_labels (JSContext *ctx, JSFunctionDef *s) {
|
|||||||
JS_ThrowOutOfMemory (ctx);
|
JS_ThrowOutOfMemory (ctx);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
/* Debug: dump bytecode for create_actor */
|
||||||
|
if (!JS_IsNull(s->func_name)) {
|
||||||
|
const char *fn = JS_ToCString(ctx, s->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("=== resolve_labels output for %s ===\n", fn);
|
||||||
|
printf("bc_len=%zu\n", bc_out.size);
|
||||||
|
for (size_t di = 0; di < bc_out.size; di++) {
|
||||||
|
printf("%02x ", bc_out.buf[di]);
|
||||||
|
if ((di + 1) % 16 == 0) printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(ctx, fn);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
/* XXX: not safe */
|
/* XXX: not safe */
|
||||||
@@ -16437,6 +16522,7 @@ fail:
|
|||||||
/* compute the maximum stack size needed by the function */
|
/* compute the maximum stack size needed by the function */
|
||||||
|
|
||||||
typedef struct StackSizeState {
|
typedef struct StackSizeState {
|
||||||
|
const uint8_t *bc_buf;
|
||||||
int bc_len;
|
int bc_len;
|
||||||
int stack_len_max;
|
int stack_len_max;
|
||||||
uint16_t *stack_level_tab;
|
uint16_t *stack_level_tab;
|
||||||
@@ -16444,10 +16530,61 @@ typedef struct StackSizeState {
|
|||||||
int *pc_stack;
|
int *pc_stack;
|
||||||
int pc_stack_len;
|
int pc_stack_len;
|
||||||
int pc_stack_size;
|
int pc_stack_size;
|
||||||
|
/* predecessor tracking for debugging */
|
||||||
|
int *pred_pc; /* predecessor PC that first set stack at pos */
|
||||||
|
int *pred_stack; /* stack height at predecessor */
|
||||||
|
uint8_t *is_op_start; /* bitmap: 1 if pos is start of instruction */
|
||||||
|
const char *func_name; /* function name for error messages */
|
||||||
} StackSizeState;
|
} StackSizeState;
|
||||||
|
|
||||||
|
static void print_backtrace_chain (StackSizeState *s, int pos, int max_depth) {
|
||||||
|
printf (" backtrace from pc=%d:\n", pos);
|
||||||
|
int depth = 0;
|
||||||
|
while (pos >= 0 && depth < max_depth) {
|
||||||
|
uint8_t op = s->bc_buf[pos];
|
||||||
|
#ifdef DUMP_BYTECODE
|
||||||
|
const JSOpCode *oi = &short_opcode_info (op);
|
||||||
|
printf (" [%d] pc=%d op=%s stack=%d\n",
|
||||||
|
depth, pos, oi->name, s->stack_level_tab[pos]);
|
||||||
|
#else
|
||||||
|
printf (" [%d] pc=%d op=%d stack=%d\n",
|
||||||
|
depth, pos, op, s->stack_level_tab[pos]);
|
||||||
|
#endif
|
||||||
|
pos = s->pred_pc[pos];
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_instructions_around (StackSizeState *s, int pos, int window) {
|
||||||
|
printf (" bytecode around pc=%d:\n", pos);
|
||||||
|
/* Find start position (scan backwards for opcode boundaries) */
|
||||||
|
int start = pos;
|
||||||
|
for (int i = 0; i < window && start > 0; ) {
|
||||||
|
start--;
|
||||||
|
if (s->is_op_start[start]) i++;
|
||||||
|
}
|
||||||
|
/* Dump instructions */
|
||||||
|
int p = start;
|
||||||
|
while (p < s->bc_len && p < pos + window * 5) {
|
||||||
|
if (!s->is_op_start[p]) { p++; continue; }
|
||||||
|
uint8_t op = s->bc_buf[p];
|
||||||
|
const JSOpCode *oi = &short_opcode_info (op);
|
||||||
|
char marker = (p == pos) ? '>' : ' ';
|
||||||
|
#ifdef DUMP_BYTECODE
|
||||||
|
printf (" %c %5d: %-20s size=%d stack_in=%d\n",
|
||||||
|
marker, p, oi->name, oi->size,
|
||||||
|
s->stack_level_tab[p] != 0xffff ? (int)s->stack_level_tab[p] : -1);
|
||||||
|
#else
|
||||||
|
printf (" %c %5d: op=%-3d size=%d stack_in=%d\n",
|
||||||
|
marker, p, op, oi->size,
|
||||||
|
s->stack_level_tab[p] != 0xffff ? (int)s->stack_level_tab[p] : -1);
|
||||||
|
#endif
|
||||||
|
p += oi->size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* 'op' is only used for error indication */
|
/* 'op' is only used for error indication */
|
||||||
static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int op, int stack_len, int catch_pos) {
|
static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int op, int stack_len, int catch_pos, int from_pos) {
|
||||||
if ((unsigned)pos >= s->bc_len) {
|
if ((unsigned)pos >= s->bc_len) {
|
||||||
JS_ThrowInternalError (ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
|
JS_ThrowInternalError (ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -16462,9 +16599,74 @@ static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int
|
|||||||
if (s->stack_level_tab[pos] != 0xffff) {
|
if (s->stack_level_tab[pos] != 0xffff) {
|
||||||
/* already explored: check that the stack size is consistent */
|
/* already explored: check that the stack size is consistent */
|
||||||
if (s->stack_level_tab[pos] != stack_len) {
|
if (s->stack_level_tab[pos] != stack_len) {
|
||||||
JS_ThrowInternalError (ctx, "inconsistent stack size: %d %d (pc=%d)", s->stack_level_tab[pos], stack_len, pos);
|
uint8_t op_at_pos = s->bc_buf[pos];
|
||||||
|
#ifdef DUMP_BYTECODE
|
||||||
|
const JSOpCode *oi = &short_opcode_info (op_at_pos);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Check if pos is a valid opcode boundary */
|
||||||
|
int boundary_valid = s->is_op_start[pos];
|
||||||
|
|
||||||
|
printf ("=== STACK ANALYSIS ERROR ===\n");
|
||||||
|
printf ("Function: %s\n", s->func_name ? s->func_name : "?");
|
||||||
|
printf ("Inconsistent stack at pc=%d:\n", pos);
|
||||||
|
printf (" expected: %d (first path)\n", s->stack_level_tab[pos]);
|
||||||
|
printf (" got: %d (conflicting path)\n", stack_len);
|
||||||
|
#ifdef DUMP_BYTECODE
|
||||||
|
printf (" opcode at pos: %s (%d)\n", oi->name, op_at_pos);
|
||||||
|
#else
|
||||||
|
printf (" opcode at pos: %d\n", op_at_pos);
|
||||||
|
#endif
|
||||||
|
printf (" valid opcode boundary: %s\n", boundary_valid ? "YES" : "NO");
|
||||||
|
|
||||||
|
printf ("\nFirst path that reached pc=%d:\n", pos);
|
||||||
|
print_backtrace_chain (s, pos, 20);
|
||||||
|
|
||||||
|
printf ("\nConflicting path (from pc=%d):\n", from_pos);
|
||||||
|
print_backtrace_chain (s, from_pos, 20);
|
||||||
|
|
||||||
|
printf ("\nBytecode window:\n");
|
||||||
|
dump_instructions_around (s, pos, 10);
|
||||||
|
|
||||||
|
#ifdef DUMP_BYTECODE
|
||||||
|
JS_ThrowInternalError (ctx,
|
||||||
|
"inconsistent stack size: %d vs %d (pc=%d, op=%s, boundary=%s)",
|
||||||
|
s->stack_level_tab[pos], stack_len, pos, oi->name,
|
||||||
|
boundary_valid ? "valid" : "INVALID");
|
||||||
|
#else
|
||||||
|
JS_ThrowInternalError (ctx,
|
||||||
|
"inconsistent stack size: %d vs %d (pc=%d, op=%d, boundary=%s)",
|
||||||
|
s->stack_level_tab[pos], stack_len, pos, op_at_pos,
|
||||||
|
boundary_valid ? "valid" : "INVALID");
|
||||||
|
#endif
|
||||||
return -1;
|
return -1;
|
||||||
} else if (s->catch_pos_tab[pos] != catch_pos) {
|
} else if (s->catch_pos_tab[pos] != catch_pos) {
|
||||||
|
uint8_t op_at_pos = s->bc_buf[pos];
|
||||||
|
#ifdef DUMP_BYTECODE
|
||||||
|
const JSOpCode *oi = &short_opcode_info (op_at_pos);
|
||||||
|
#endif
|
||||||
|
int boundary_valid = s->is_op_start[pos];
|
||||||
|
|
||||||
|
printf ("=== CATCH POSITION ERROR ===\n");
|
||||||
|
printf ("Inconsistent catch position at pc=%d:\n", pos);
|
||||||
|
printf (" expected: %d (first path)\n", s->catch_pos_tab[pos]);
|
||||||
|
printf (" got: %d (conflicting path)\n", catch_pos);
|
||||||
|
#ifdef DUMP_BYTECODE
|
||||||
|
printf (" opcode at pos: %s (%d)\n", oi->name, op_at_pos);
|
||||||
|
#else
|
||||||
|
printf (" opcode at pos: %d\n", op_at_pos);
|
||||||
|
#endif
|
||||||
|
printf (" valid opcode boundary: %s\n", boundary_valid ? "YES" : "NO");
|
||||||
|
|
||||||
|
printf ("\nFirst path that reached pc=%d:\n", pos);
|
||||||
|
print_backtrace_chain (s, pos, 20);
|
||||||
|
|
||||||
|
printf ("\nConflicting path (from pc=%d):\n", from_pos);
|
||||||
|
print_backtrace_chain (s, from_pos, 20);
|
||||||
|
|
||||||
|
printf ("\nBytecode window:\n");
|
||||||
|
dump_instructions_around (s, pos, 10);
|
||||||
|
|
||||||
JS_ThrowInternalError (ctx, "inconsistent catch position: %d %d (pc=%d)", s->catch_pos_tab[pos], catch_pos, pos);
|
JS_ThrowInternalError (ctx, "inconsistent catch position: %d %d (pc=%d)", s->catch_pos_tab[pos], catch_pos, pos);
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
@@ -16475,6 +16677,9 @@ static __exception int ss_check (JSContext *ctx, StackSizeState *s, int pos, int
|
|||||||
/* mark as explored and store the stack size */
|
/* mark as explored and store the stack size */
|
||||||
s->stack_level_tab[pos] = stack_len;
|
s->stack_level_tab[pos] = stack_len;
|
||||||
s->catch_pos_tab[pos] = catch_pos;
|
s->catch_pos_tab[pos] = catch_pos;
|
||||||
|
s->pred_pc[pos] = from_pos;
|
||||||
|
s->pred_stack[pos] = stack_len;
|
||||||
|
s->is_op_start[pos] = 1;
|
||||||
|
|
||||||
/* queue the new PC to explore */
|
/* queue the new PC to explore */
|
||||||
if (pjs_resize_array ((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))
|
||||||
@@ -16490,7 +16695,10 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
|||||||
const uint8_t *bc_buf;
|
const uint8_t *bc_buf;
|
||||||
|
|
||||||
bc_buf = fd->byte_code.buf;
|
bc_buf = fd->byte_code.buf;
|
||||||
|
s->bc_buf = bc_buf;
|
||||||
s->bc_len = fd->byte_code.size;
|
s->bc_len = fd->byte_code.size;
|
||||||
|
s->func_name = JS_IsNull(fd->func_name) ? "?" : JS_ToCString(ctx, fd->func_name);
|
||||||
|
|
||||||
/* bc_len > 0 */
|
/* bc_len > 0 */
|
||||||
s->stack_level_tab
|
s->stack_level_tab
|
||||||
= pjs_malloc (sizeof (s->stack_level_tab[0]) * s->bc_len);
|
= pjs_malloc (sizeof (s->stack_level_tab[0]) * s->bc_len);
|
||||||
@@ -16501,12 +16709,24 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
|||||||
s->catch_pos_tab = pjs_malloc (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;
|
if (!s->catch_pos_tab) goto fail;
|
||||||
|
|
||||||
|
/* allocate predecessor tracking arrays */
|
||||||
|
s->pred_pc = pjs_malloc (sizeof (s->pred_pc[0]) * s->bc_len);
|
||||||
|
if (!s->pred_pc) goto fail;
|
||||||
|
for (i = 0; i < s->bc_len; i++) s->pred_pc[i] = -1;
|
||||||
|
|
||||||
|
s->pred_stack = pjs_malloc (sizeof (s->pred_stack[0]) * s->bc_len);
|
||||||
|
if (!s->pred_stack) goto fail;
|
||||||
|
|
||||||
|
s->is_op_start = pjs_malloc (sizeof (s->is_op_start[0]) * s->bc_len);
|
||||||
|
if (!s->is_op_start) goto fail;
|
||||||
|
memset (s->is_op_start, 0, s->bc_len);
|
||||||
|
|
||||||
s->stack_len_max = 0;
|
s->stack_len_max = 0;
|
||||||
s->pc_stack_len = 0;
|
s->pc_stack_len = 0;
|
||||||
s->pc_stack_size = 0;
|
s->pc_stack_size = 0;
|
||||||
|
|
||||||
/* breadth-first graph exploration */
|
/* breadth-first graph exploration */
|
||||||
if (ss_check (ctx, s, 0, OP_invalid, 0, -1)) goto fail;
|
if (ss_check (ctx, s, 0, OP_invalid, 0, -1, -1)) goto fail;
|
||||||
|
|
||||||
while (s->pc_stack_len > 0) {
|
while (s->pc_stack_len > 0) {
|
||||||
pos = s->pc_stack[--s->pc_stack_len];
|
pos = s->pc_stack[--s->pc_stack_len];
|
||||||
@@ -16514,13 +16734,18 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
|||||||
catch_pos = s->catch_pos_tab[pos];
|
catch_pos = s->catch_pos_tab[pos];
|
||||||
op = bc_buf[pos];
|
op = bc_buf[pos];
|
||||||
if (op == 0 || op >= OP_COUNT) {
|
if (op == 0 || op >= OP_COUNT) {
|
||||||
JS_ThrowInternalError (ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
|
char fn_buf[64] = "?";
|
||||||
|
if (!JS_IsNull (fd->func_name)) {
|
||||||
|
const char *fn = JS_ToCString (ctx, fd->func_name);
|
||||||
|
if (fn) {
|
||||||
|
snprintf (fn_buf, sizeof (fn_buf), "%s", fn);
|
||||||
|
JS_FreeCString (ctx, fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JS_ThrowInternalError (ctx, "invalid opcode in '%s' (op=%d, pc=%d, bc_len=%d)", fn_buf, op, pos, s->bc_len);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
oi = &short_opcode_info (op);
|
oi = &short_opcode_info (op);
|
||||||
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
|
|
||||||
printf ("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
|
|
||||||
#endif
|
|
||||||
pos_next = pos + oi->size;
|
pos_next = pos + oi->size;
|
||||||
if (pos_next > s->bc_len) {
|
if (pos_next > s->bc_len) {
|
||||||
JS_ThrowInternalError (ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
|
JS_ThrowInternalError (ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
|
||||||
@@ -16573,24 +16798,24 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
|||||||
case OP_if_true8:
|
case OP_if_true8:
|
||||||
case OP_if_false8:
|
case OP_if_false8:
|
||||||
diff = (int8_t)bc_buf[pos + 1];
|
diff = (int8_t)bc_buf[pos + 1];
|
||||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
|
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos, pos))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case OP_if_true:
|
case OP_if_true:
|
||||||
case OP_if_false:
|
case OP_if_false:
|
||||||
diff = get_u32 (bc_buf + pos + 1);
|
diff = get_u32 (bc_buf + pos + 1);
|
||||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
|
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos, pos))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case OP_gosub:
|
case OP_gosub:
|
||||||
diff = get_u32 (bc_buf + pos + 1);
|
diff = get_u32 (bc_buf + pos + 1);
|
||||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos))
|
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos, pos))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
case OP_catch:
|
case OP_catch:
|
||||||
diff = get_u32 (bc_buf + pos + 1);
|
diff = get_u32 (bc_buf + pos + 1);
|
||||||
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
|
if (ss_check (ctx, s, pos + 1 + diff, op, stack_len, catch_pos, pos))
|
||||||
goto fail;
|
goto fail;
|
||||||
catch_pos = pos;
|
catch_pos = pos;
|
||||||
break;
|
break;
|
||||||
@@ -16624,18 +16849,24 @@ static __exception int compute_stack_size (JSContext *ctx, JSFunctionDef *fd, in
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ss_check (ctx, s, pos_next, op, stack_len, catch_pos)) goto fail;
|
if (ss_check (ctx, s, pos_next, op, stack_len, catch_pos, pos)) goto fail;
|
||||||
done_insn:;
|
done_insn:;
|
||||||
}
|
}
|
||||||
pjs_free (s->pc_stack);
|
pjs_free (s->pc_stack);
|
||||||
pjs_free (s->catch_pos_tab);
|
pjs_free (s->catch_pos_tab);
|
||||||
pjs_free (s->stack_level_tab);
|
pjs_free (s->stack_level_tab);
|
||||||
|
pjs_free (s->pred_pc);
|
||||||
|
pjs_free (s->pred_stack);
|
||||||
|
pjs_free (s->is_op_start);
|
||||||
*pstack_size = s->stack_len_max;
|
*pstack_size = s->stack_len_max;
|
||||||
return 0;
|
return 0;
|
||||||
fail:
|
fail:
|
||||||
pjs_free (s->pc_stack);
|
pjs_free (s->pc_stack);
|
||||||
pjs_free (s->catch_pos_tab);
|
pjs_free (s->catch_pos_tab);
|
||||||
pjs_free (s->stack_level_tab);
|
pjs_free (s->stack_level_tab);
|
||||||
|
pjs_free (s->pred_pc);
|
||||||
|
pjs_free (s->pred_stack);
|
||||||
|
pjs_free (s->is_op_start);
|
||||||
*pstack_size = 0;
|
*pstack_size = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -16651,6 +16882,15 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
|||||||
int function_size, byte_code_offset, cpool_offset;
|
int function_size, byte_code_offset, cpool_offset;
|
||||||
int closure_var_offset, vardefs_offset;
|
int closure_var_offset, vardefs_offset;
|
||||||
|
|
||||||
|
/* Debug: check initial bytecode size */
|
||||||
|
if (!JS_IsNull(fd->func_name)) {
|
||||||
|
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: js_create_function START, bc_size=%zu\n", fd->byte_code.size);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(ctx, fn);
|
||||||
|
}
|
||||||
|
|
||||||
/* recompute scope linkage */
|
/* recompute scope linkage */
|
||||||
for (scope = 0; scope < fd->scope_count; scope++) {
|
for (scope = 0; scope < fd->scope_count; scope++) {
|
||||||
fd->scopes[scope].first = -1;
|
fd->scopes[scope].first = -1;
|
||||||
@@ -16698,7 +16938,23 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Debug: check bytecode size before resolve_variables */
|
||||||
|
if (!JS_IsNull(fd->func_name)) {
|
||||||
|
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: before resolve_variables, bc_size=%zu\n", fd->byte_code.size);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(ctx, fn);
|
||||||
|
}
|
||||||
if (resolve_variables (ctx, fd)) goto fail;
|
if (resolve_variables (ctx, fd)) goto fail;
|
||||||
|
/* Debug: check bytecode size after resolve_variables */
|
||||||
|
if (!JS_IsNull(fd->func_name)) {
|
||||||
|
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: after resolve_variables, bc_size=%zu\n", fd->byte_code.size);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(ctx, fn);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
|
#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
|
||||||
if (!fd->strip_debug) {
|
if (!fd->strip_debug) {
|
||||||
@@ -16708,6 +16964,14 @@ static JSValue js_create_function (JSContext *ctx, JSFunctionDef *fd) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Debug: check bytecode size before resolve_labels */
|
||||||
|
if (!JS_IsNull(fd->func_name)) {
|
||||||
|
const char *fn = JS_ToCString(ctx, fd->func_name);
|
||||||
|
if (fn && strcmp(fn, "create_actor") == 0) {
|
||||||
|
printf("DEBUG: before resolve_labels, bc_size=%zu\n", fd->byte_code.size);
|
||||||
|
}
|
||||||
|
if (fn) JS_FreeCString(ctx, fn);
|
||||||
|
}
|
||||||
if (resolve_labels (ctx, fd)) goto fail;
|
if (resolve_labels (ctx, fd)) goto fail;
|
||||||
|
|
||||||
if (compute_stack_size (ctx, fd, &stack_size) < 0) goto fail;
|
if (compute_stack_size (ctx, fd, &stack_size) < 0) goto fail;
|
||||||
|
|||||||
Reference in New Issue
Block a user