Skip to content

Commit 553dee3

Browse files
committed
Handle bytecode without IC state
Deserialized bytecode does not have IC state, i.e., `bc->ic == NULL`. That may or may not be bug (IMO, it is and we should rebuild the IC state during deserialization) but, either way, don't segfault. DRY add_ic_slot() and its call sites in a hopefully NFC manner.
1 parent 8b2adcd commit 553dee3

File tree

3 files changed

+37
-24
lines changed

3 files changed

+37
-24
lines changed

quickjs-libc.c

+17-6
Original file line numberDiff line numberDiff line change
@@ -832,11 +832,12 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
832832
{
833833
JSRuntime *rt = JS_GetRuntime(ctx);
834834
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
835-
const char *str;
835+
const char *str = NULL;
836836
size_t len;
837-
JSValue ret;
837+
JSValue ret, obj;
838838
JSValue options_obj;
839839
BOOL backtrace_barrier = FALSE;
840+
BOOL eval_function = FALSE;
840841
BOOL compile_only = FALSE;
841842
BOOL is_async = FALSE;
842843
int flags;
@@ -846,6 +847,9 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
846847
if (get_bool_option(ctx, &backtrace_barrier, options_obj,
847848
"backtrace_barrier"))
848849
return JS_EXCEPTION;
850+
if (get_bool_option(ctx, &eval_function, options_obj,
851+
"eval_function"))
852+
return JS_EXCEPTION;
849853
if (get_bool_option(ctx, &compile_only, options_obj,
850854
"compile_only"))
851855
return JS_EXCEPTION;
@@ -854,9 +858,11 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
854858
return JS_EXCEPTION;
855859
}
856860

857-
str = JS_ToCStringLen(ctx, &len, argv[0]);
858-
if (!str)
859-
return JS_EXCEPTION;
861+
if (!eval_function) {
862+
str = JS_ToCStringLen(ctx, &len, argv[0]);
863+
if (!str)
864+
return JS_EXCEPTION;
865+
}
860866
if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) {
861867
/* install the interrupt handler */
862868
JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);
@@ -868,7 +874,12 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
868874
flags |= JS_EVAL_FLAG_COMPILE_ONLY;
869875
if (is_async)
870876
flags |= JS_EVAL_FLAG_ASYNC;
871-
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
877+
if (eval_function) {
878+
obj = JS_DupValue(ctx, argv[0]);
879+
ret = JS_EvalFunction(ctx, obj); // takes ownership of |obj|
880+
} else {
881+
ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
882+
}
872883
JS_FreeCString(ctx, str);
873884
if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
874885
/* remove the interrupt handler */

quickjs.c

+17-15
Original file line numberDiff line numberDiff line change
@@ -7362,9 +7362,8 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj,
73627362
continue;
73637363
}
73647364
} else {
7365-
if (icu && proto_depth == 0 && p->shape->is_hashed) {
7365+
if (proto_depth == 0)
73667366
add_ic_slot(ctx, icu, prop, p, offset);
7367-
}
73687367
return js_dup(pr->u.value);
73697368
}
73707369
}
@@ -8658,9 +8657,7 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop,
86588657
if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
86598658
JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
86608659
/* fast case */
8661-
if (icu && p->shape->is_hashed) {
8662-
add_ic_slot(ctx, icu, prop, p, offset);
8663-
}
8660+
add_ic_slot(ctx, icu, prop, p, offset);
86648661
set_value(ctx, &pr->u.value, val);
86658662
return TRUE;
86668663
} else if (prs->flags & JS_PROP_LENGTH) {
@@ -54516,10 +54513,19 @@ static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu,
5451654513
{
5451754514
int32_t i;
5451854515
uint32_t h;
54519-
JSInlineCache *ic = icu->ic;
5452054516
JSInlineCacheHashSlot *ch;
5452154517
JSInlineCacheRingSlot *cr;
54518+
JSInlineCache *ic;
5452254519
JSShape *sh;
54520+
54521+
if (!icu)
54522+
return;
54523+
ic = icu->ic;
54524+
if (!ic)
54525+
return;
54526+
sh = object->shape;
54527+
if (!sh->is_hashed)
54528+
return;
5452354529
cr = NULL;
5452454530
h = get_index_hash(atom, ic->hash_bits);
5452554531
for (ch = ic->hash[h]; ch != NULL; ch = ch->next) {
@@ -54528,21 +54534,17 @@ static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu,
5452854534
break;
5452954535
}
5453054536
}
54531-
5453254537
assert(cr != NULL);
5453354538
i = cr->index;
54534-
for (;;) {
54535-
if (object->shape == cr->shape[i]) {
54539+
do {
54540+
if (sh == cr->shape[i]) {
5453654541
cr->prop_offset[i] = prop_offset;
5453754542
goto end;
5453854543
}
5453954544
i = (i + 1) % countof(cr->shape);
54540-
if (unlikely(i == cr->index))
54541-
break;
54542-
}
54543-
sh = cr->shape[i];
54544-
cr->shape[i] = js_dup_shape(object->shape);
54545-
js_free_shape_null(ctx->rt, sh);
54545+
} while (i != cr->index);
54546+
js_free_shape_null(ctx->rt, cr->shape[i]);
54547+
cr->shape[i] = js_dup_shape(sh);
5454654548
cr->prop_offset[i] = prop_offset;
5454754549
end:
5454854550
icu->offset = ch->index;

tests/test_bjson.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ function bjson_test_symbol()
230230

231231
function bjson_test_bytecode()
232232
{
233-
var buf, o, r, e;
233+
var buf, o, r, e, i;
234234

235235
o = std.evalScript(";(function f(o){ return o.i })", {compile_only: true});
236236
buf = bjson.write(o, /*JS_WRITE_OBJ_BYTECODE*/(1 << 0));
@@ -241,10 +241,10 @@ function bjson_test_bytecode()
241241
}
242242
assert(String(e), "SyntaxError: no bytecode allowed");
243243

244-
// can't really do anything with |o| at the moment,
245-
// no way to pass it to JS_EvalFunction
246244
o = bjson.read(buf, 0, buf.byteLength, /*JS_READ_OBJ_BYTECODE*/(1 << 0));
247245
assert(String(o), "[function bytecode]");
246+
o = std.evalScript(o, {eval_function: true});
247+
for (i = 0; i < 42; i++) o({i}); // exercise o.i IC
248248
}
249249

250250
function bjson_test_fuzz()

0 commit comments

Comments
 (0)