70
70
8: dump stdlib functions
71
71
16: dump bytecode in hex
72
72
32: dump line number table
73
- 64: dump executed bytecode
73
+ 64: dump compute_stack_size
74
+ 128: dump executed bytecode
74
75
*/
75
76
//#define DUMP_BYTECODE (1)
76
77
/* dump the occurence of the automatic GC */
@@ -14354,7 +14355,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
14354
14355
size_t alloca_size;
14355
14356
JSInlineCache *ic;
14356
14357
14357
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64 )
14358
+ #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 128 )
14358
14359
#define DUMP_BYTECODE_OR_DONT(pc) dump_single_byte_code(ctx, pc, b);
14359
14360
#else
14360
14361
#define DUMP_BYTECODE_OR_DONT(pc)
@@ -14463,7 +14464,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
14463
14464
ctx = b->realm; /* set the current realm */
14464
14465
ic = b->ic;
14465
14466
14466
- #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64 )
14467
+ #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 128 )
14467
14468
print_func_name(b);
14468
14469
#endif
14469
14470
@@ -15599,26 +15600,21 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj,
15599
15600
}
15600
15601
sp--;
15601
15602
BREAK;
15602
- CASE(OP_iterator_close_return ):
15603
+ CASE(OP_nip_catch ):
15603
15604
{
15604
15605
JSValue ret_val;
15605
- /* iter_obj next catch_offset ... ret_val ->
15606
- ret_eval iter_obj next catch_offset */
15606
+ /* catch_offset ... ret_val -> ret_eval */
15607
15607
ret_val = *--sp;
15608
15608
while (sp > stack_buf &&
15609
15609
JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
15610
15610
JS_FreeValue(ctx, *--sp);
15611
15611
}
15612
- if (unlikely(sp < stack_buf + 3 )) {
15613
- JS_ThrowInternalError(ctx, "iterator_close_return ");
15612
+ if (unlikely(sp == stack_buf)) {
15613
+ JS_ThrowInternalError(ctx, "nip_catch ");
15614
15614
JS_FreeValue(ctx, ret_val);
15615
15615
goto exception;
15616
15616
}
15617
- sp[0] = sp[-1];
15618
- sp[-1] = sp[-2];
15619
- sp[-2] = sp[-3];
15620
- sp[-3] = ret_val;
15621
- sp++;
15617
+ sp[-1] = ret_val;
15622
15618
}
15623
15619
BREAK;
15624
15620
@@ -23876,7 +23872,6 @@ static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
23876
23872
static void emit_return(JSParseState *s, BOOL hasval)
23877
23873
{
23878
23874
BlockEnv *top;
23879
- int drop_count;
23880
23875
23881
23876
if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
23882
23877
if (!hasval) {
@@ -23890,60 +23885,49 @@ static void emit_return(JSParseState *s, BOOL hasval)
23890
23885
}
23891
23886
}
23892
23887
23893
- drop_count = 0;
23894
23888
top = s->cur_func->top_break;
23895
23889
while (top != NULL) {
23896
- /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not
23897
- required as all local variables will be closed upon returning
23898
- from JS_CallInternal, but not in the same order. */
23899
- if (top->has_iterator) {
23900
- /* with 'yield', the exact number of OP_drop to emit is
23901
- unknown, so we use a specific operation to look for
23902
- the catch offset */
23890
+ if (top->has_iterator || top->label_finally != -1) {
23903
23891
if (!hasval) {
23904
23892
emit_op(s, OP_undefined);
23905
23893
hasval = TRUE;
23906
23894
}
23907
- emit_op(s, OP_iterator_close_return);
23908
- if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
23909
- int label_next, label_next2;
23910
-
23911
- emit_op(s, OP_drop); /* catch offset */
23912
- emit_op(s, OP_drop); /* next */
23913
- emit_op(s, OP_get_field2);
23914
- emit_atom(s, JS_ATOM_return);
23915
- emit_ic(s, JS_ATOM_return);
23916
- /* stack: iter_obj return_func */
23917
- emit_op(s, OP_dup);
23918
- emit_op(s, OP_is_undefined_or_null);
23919
- label_next = emit_goto(s, OP_if_true, -1);
23920
- emit_op(s, OP_call_method);
23921
- emit_u16(s, 0);
23922
- emit_op(s, OP_iterator_check_object);
23923
- emit_op(s, OP_await);
23924
- label_next2 = emit_goto(s, OP_goto, -1);
23925
- emit_label(s, label_next);
23926
- emit_op(s, OP_drop);
23927
- emit_label(s, label_next2);
23928
- emit_op(s, OP_drop);
23895
+ /* Remove the stack elements up to and including the catch
23896
+ offset. When 'yield' is used in an expression we have
23897
+ no easy way to count them, so we use this specific
23898
+ instruction instead. */
23899
+ emit_op(s, OP_nip_catch);
23900
+ /* stack: iter_obj next ret_val */
23901
+ if (top->has_iterator) {
23902
+ if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
23903
+ int label_next, label_next2;
23904
+ emit_op(s, OP_nip); /* next */
23905
+ emit_op(s, OP_swap);
23906
+ emit_op(s, OP_get_field2);
23907
+ emit_atom(s, JS_ATOM_return);
23908
+ emit_ic(s, JS_ATOM_return);
23909
+ /* stack: iter_obj return_func */
23910
+ emit_op(s, OP_dup);
23911
+ emit_op(s, OP_is_undefined_or_null);
23912
+ label_next = emit_goto(s, OP_if_true, -1);
23913
+ emit_op(s, OP_call_method);
23914
+ emit_u16(s, 0);
23915
+ emit_op(s, OP_iterator_check_object);
23916
+ emit_op(s, OP_await);
23917
+ label_next2 = emit_goto(s, OP_goto, -1);
23918
+ emit_label(s, label_next);
23919
+ emit_op(s, OP_drop);
23920
+ emit_label(s, label_next2);
23921
+ emit_op(s, OP_drop);
23922
+ } else {
23923
+ emit_op(s, OP_rot3r);
23924
+ emit_op(s, OP_undefined); /* dummy catch offset */
23925
+ emit_op(s, OP_iterator_close);
23926
+ }
23929
23927
} else {
23930
- emit_op(s, OP_iterator_close);
23931
- }
23932
- drop_count = -3;
23933
- }
23934
- drop_count += top->drop_count;
23935
- if (top->label_finally != -1) {
23936
- while(drop_count) {
23937
- /* must keep the stack top if hasval */
23938
- emit_op(s, hasval ? OP_nip : OP_drop);
23939
- drop_count--;
23928
+ /* execute the "finally" block */
23929
+ emit_goto(s, OP_gosub, top->label_finally);
23940
23930
}
23941
- if (!hasval) {
23942
- /* must push return value to keep same stack size */
23943
- emit_op(s, OP_undefined);
23944
- hasval = TRUE;
23945
- }
23946
- emit_goto(s, OP_gosub, top->label_finally);
23947
23931
}
23948
23932
top = top->prev;
23949
23933
}
@@ -30455,14 +30439,15 @@ typedef struct StackSizeState {
30455
30439
int bc_len;
30456
30440
int stack_len_max;
30457
30441
uint16_t *stack_level_tab;
30442
+ int32_t *catch_pos_tab;
30458
30443
int *pc_stack;
30459
30444
int pc_stack_len;
30460
30445
int pc_stack_size;
30461
30446
} StackSizeState;
30462
30447
30463
30448
/* 'op' is only used for error indication */
30464
30449
static __exception int ss_check(JSContext *ctx, StackSizeState *s,
30465
- int pos, int op, int stack_len)
30450
+ int pos, int op, int stack_len, int catch_pos )
30466
30451
{
30467
30452
if ((unsigned)pos >= s->bc_len) {
30468
30453
JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
@@ -30481,13 +30466,18 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s,
30481
30466
JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)",
30482
30467
s->stack_level_tab[pos], stack_len, pos);
30483
30468
return -1;
30469
+ } else if (s->catch_pos_tab[pos] != catch_pos) {
30470
+ JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)",
30471
+ s->catch_pos_tab[pos], catch_pos, pos);
30472
+ return -1;
30484
30473
} else {
30485
30474
return 0;
30486
30475
}
30487
30476
}
30488
30477
30489
30478
/* mark as explored and store the stack size */
30490
30479
s->stack_level_tab[pos] = stack_len;
30480
+ s->catch_pos_tab[pos] = catch_pos;
30491
30481
30492
30482
/* queue the new PC to explore */
30493
30483
if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
@@ -30502,7 +30492,7 @@ static __exception int compute_stack_size(JSContext *ctx,
30502
30492
int *pstack_size)
30503
30493
{
30504
30494
StackSizeState s_s, *s = &s_s;
30505
- int i, diff, n_pop, pos_next, stack_len, pos, op;
30495
+ int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level ;
30506
30496
const JSOpCode *oi;
30507
30497
const uint8_t *bc_buf;
30508
30498
@@ -30515,24 +30505,32 @@ static __exception int compute_stack_size(JSContext *ctx,
30515
30505
return -1;
30516
30506
for(i = 0; i < s->bc_len; i++)
30517
30507
s->stack_level_tab[i] = 0xffff;
30518
- s->stack_len_max = 0;
30519
30508
s->pc_stack = NULL;
30509
+ s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) * s->bc_len);
30510
+ if (!s->catch_pos_tab)
30511
+ goto fail;
30512
+
30513
+ s->stack_len_max = 0;
30520
30514
s->pc_stack_len = 0;
30521
30515
s->pc_stack_size = 0;
30522
30516
30523
30517
/* breadth-first graph exploration */
30524
- if (ss_check(ctx, s, 0, OP_invalid, 0))
30518
+ if (ss_check(ctx, s, 0, OP_invalid, 0, -1 ))
30525
30519
goto fail;
30526
30520
30527
30521
while (s->pc_stack_len > 0) {
30528
30522
pos = s->pc_stack[--s->pc_stack_len];
30529
30523
stack_len = s->stack_level_tab[pos];
30524
+ catch_pos = s->catch_pos_tab[pos];
30530
30525
op = bc_buf[pos];
30531
30526
if (op == 0 || op >= OP_COUNT) {
30532
30527
JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
30533
30528
goto fail;
30534
30529
}
30535
30530
oi = &short_opcode_info(op);
30531
+ #if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64)
30532
+ printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos);
30533
+ #endif
30536
30534
pos_next = pos + oi->size;
30537
30535
if (pos_next > s->bc_len) {
30538
30536
JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
@@ -30583,54 +30581,103 @@ static __exception int compute_stack_size(JSContext *ctx,
30583
30581
case OP_if_true8:
30584
30582
case OP_if_false8:
30585
30583
diff = (int8_t)bc_buf[pos + 1];
30586
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
30584
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos ))
30587
30585
goto fail;
30588
30586
break;
30589
30587
case OP_if_true:
30590
30588
case OP_if_false:
30591
- case OP_catch:
30592
30589
diff = get_u32(bc_buf + pos + 1);
30593
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
30590
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos ))
30594
30591
goto fail;
30595
30592
break;
30596
30593
case OP_gosub:
30597
30594
diff = get_u32(bc_buf + pos + 1);
30598
- if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))
30595
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos ))
30599
30596
goto fail;
30600
30597
break;
30601
30598
case OP_with_get_var:
30602
30599
case OP_with_delete_var:
30603
30600
diff = get_u32(bc_buf + pos + 5);
30604
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))
30601
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos ))
30605
30602
goto fail;
30606
30603
break;
30607
30604
case OP_with_make_ref:
30608
30605
case OP_with_get_ref:
30609
30606
case OP_with_get_ref_undef:
30610
30607
diff = get_u32(bc_buf + pos + 5);
30611
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))
30608
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos ))
30612
30609
goto fail;
30613
30610
break;
30614
30611
case OP_with_put_var:
30615
30612
diff = get_u32(bc_buf + pos + 5);
30616
- if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))
30613
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos ))
30617
30614
goto fail;
30618
30615
break;
30619
-
30616
+ case OP_catch:
30617
+ diff = get_u32(bc_buf + pos + 1);
30618
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos))
30619
+ goto fail;
30620
+ catch_pos = pos;
30621
+ break;
30622
+ case OP_for_of_start:
30623
+ case OP_for_await_of_start:
30624
+ catch_pos = pos;
30625
+ break;
30626
+ /* we assume the catch offset entry is only removed with
30627
+ some op codes */
30628
+ case OP_drop:
30629
+ catch_level = stack_len;
30630
+ goto check_catch;
30631
+ case OP_nip:
30632
+ catch_level = stack_len - 1;
30633
+ goto check_catch;
30634
+ case OP_nip1:
30635
+ catch_level = stack_len - 1;
30636
+ goto check_catch;
30637
+ case OP_iterator_close:
30638
+ catch_level = stack_len + 2;
30639
+ check_catch:
30640
+ /* Note: for for_of_start/for_await_of_start we consider
30641
+ the catch offset is on the first stack entry instead of
30642
+ the thirst */
30643
+ if (catch_pos >= 0) {
30644
+ int level;
30645
+ level = s->stack_level_tab[catch_pos];
30646
+ if (bc_buf[catch_pos] != OP_catch)
30647
+ level++; /* for_of_start, for_wait_of_start */
30648
+ /* catch_level = stack_level before op_catch is executed ? */
30649
+ if (catch_level == level) {
30650
+ catch_pos = s->catch_pos_tab[catch_pos];
30651
+ }
30652
+ }
30653
+ break;
30654
+ case OP_nip_catch:
30655
+ if (catch_pos < 0) {
30656
+ JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos);
30657
+ goto fail;
30658
+ }
30659
+ stack_len = s->stack_level_tab[catch_pos];
30660
+ if (bc_buf[catch_pos] != OP_catch)
30661
+ stack_len++; /* for_of_start, for_wait_of_start */
30662
+ stack_len++; /* no stack overflow is possible by construction */
30663
+ catch_pos = s->catch_pos_tab[catch_pos];
30664
+ break;
30620
30665
default:
30621
30666
break;
30622
30667
}
30623
- if (ss_check(ctx, s, pos_next, op, stack_len))
30668
+ if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos ))
30624
30669
goto fail;
30625
30670
done_insn: ;
30626
30671
}
30627
- js_free(ctx, s->stack_level_tab);
30628
30672
js_free(ctx, s->pc_stack);
30673
+ js_free(ctx, s->catch_pos_tab);
30674
+ js_free(ctx, s->stack_level_tab);
30629
30675
*pstack_size = s->stack_len_max;
30630
30676
return 0;
30631
30677
fail:
30632
- js_free(ctx, s->stack_level_tab);
30633
30678
js_free(ctx, s->pc_stack);
30679
+ js_free(ctx, s->catch_pos_tab);
30680
+ js_free(ctx, s->stack_level_tab);
30634
30681
*pstack_size = 0;
30635
30682
return -1;
30636
30683
}
0 commit comments