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.
This commit is contained in:
Ben Noordhuis 2024-10-24 09:09:40 +02:00
parent 0a79b84ef9
commit caa1bf544d
3 changed files with 37 additions and 24 deletions

View file

@ -832,11 +832,12 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
{ {
JSRuntime *rt = JS_GetRuntime(ctx); JSRuntime *rt = JS_GetRuntime(ctx);
JSThreadState *ts = JS_GetRuntimeOpaque(rt); JSThreadState *ts = JS_GetRuntimeOpaque(rt);
const char *str; const char *str = NULL;
size_t len; size_t len;
JSValue ret; JSValue ret, obj;
JSValue options_obj; JSValue options_obj;
BOOL backtrace_barrier = FALSE; BOOL backtrace_barrier = FALSE;
BOOL eval_function = FALSE;
BOOL compile_only = FALSE; BOOL compile_only = FALSE;
BOOL is_async = FALSE; BOOL is_async = FALSE;
int flags; int flags;
@ -846,6 +847,9 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
if (get_bool_option(ctx, &backtrace_barrier, options_obj, if (get_bool_option(ctx, &backtrace_barrier, options_obj,
"backtrace_barrier")) "backtrace_barrier"))
return JS_EXCEPTION; return JS_EXCEPTION;
if (get_bool_option(ctx, &eval_function, options_obj,
"eval_function"))
return JS_EXCEPTION;
if (get_bool_option(ctx, &compile_only, options_obj, if (get_bool_option(ctx, &compile_only, options_obj,
"compile_only")) "compile_only"))
return JS_EXCEPTION; return JS_EXCEPTION;
@ -854,9 +858,11 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
return JS_EXCEPTION; return JS_EXCEPTION;
} }
if (!eval_function) {
str = JS_ToCStringLen(ctx, &len, argv[0]); str = JS_ToCStringLen(ctx, &len, argv[0]);
if (!str) if (!str)
return JS_EXCEPTION; return JS_EXCEPTION;
}
if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) {
/* install the interrupt handler */ /* install the interrupt handler */
JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL);
@ -868,7 +874,12 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
flags |= JS_EVAL_FLAG_COMPILE_ONLY; flags |= JS_EVAL_FLAG_COMPILE_ONLY;
if (is_async) if (is_async)
flags |= JS_EVAL_FLAG_ASYNC; flags |= JS_EVAL_FLAG_ASYNC;
if (eval_function) {
obj = JS_DupValue(ctx, argv[0]);
ret = JS_EvalFunction(ctx, obj); // takes ownership of |obj|
} else {
ret = JS_Eval(ctx, str, len, "<evalScript>", flags); ret = JS_Eval(ctx, str, len, "<evalScript>", flags);
}
JS_FreeCString(ctx, str); JS_FreeCString(ctx, str);
if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { if (!ts->recv_pipe && --ts->eval_script_recurse == 0) {
/* remove the interrupt handler */ /* remove the interrupt handler */

View file

@ -7362,9 +7362,8 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj,
continue; continue;
} }
} else { } else {
if (icu && proto_depth == 0 && p->shape->is_hashed) { if (proto_depth == 0)
add_ic_slot(ctx, icu, prop, p, offset); add_ic_slot(ctx, icu, prop, p, offset);
}
return js_dup(pr->u.value); return js_dup(pr->u.value);
} }
} }
@ -8658,9 +8657,7 @@ retry:
if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
/* fast case */ /* fast case */
if (icu && p->shape->is_hashed) {
add_ic_slot(ctx, icu, prop, p, offset); add_ic_slot(ctx, icu, prop, p, offset);
}
set_value(ctx, &pr->u.value, val); set_value(ctx, &pr->u.value, val);
return TRUE; return TRUE;
} else if (prs->flags & JS_PROP_LENGTH) { } else if (prs->flags & JS_PROP_LENGTH) {
@ -54516,10 +54513,19 @@ static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu,
{ {
int32_t i; int32_t i;
uint32_t h; uint32_t h;
JSInlineCache *ic = icu->ic;
JSInlineCacheHashSlot *ch; JSInlineCacheHashSlot *ch;
JSInlineCacheRingSlot *cr; JSInlineCacheRingSlot *cr;
JSInlineCache *ic;
JSShape *sh; JSShape *sh;
if (!icu)
return;
ic = icu->ic;
if (!ic)
return;
sh = object->shape;
if (!sh->is_hashed)
return;
cr = NULL; cr = NULL;
h = get_index_hash(atom, ic->hash_bits); h = get_index_hash(atom, ic->hash_bits);
for (ch = ic->hash[h]; ch != NULL; ch = ch->next) { for (ch = ic->hash[h]; ch != NULL; ch = ch->next) {
@ -54528,21 +54534,17 @@ static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu,
break; break;
} }
} }
assert(cr != NULL); assert(cr != NULL);
i = cr->index; i = cr->index;
for (;;) { do {
if (object->shape == cr->shape[i]) { if (sh == cr->shape[i]) {
cr->prop_offset[i] = prop_offset; cr->prop_offset[i] = prop_offset;
goto end; goto end;
} }
i = (i + 1) % countof(cr->shape); i = (i + 1) % countof(cr->shape);
if (unlikely(i == cr->index)) } while (i != cr->index);
break; js_free_shape_null(ctx->rt, cr->shape[i]);
} cr->shape[i] = js_dup_shape(sh);
sh = cr->shape[i];
cr->shape[i] = js_dup_shape(object->shape);
js_free_shape_null(ctx->rt, sh);
cr->prop_offset[i] = prop_offset; cr->prop_offset[i] = prop_offset;
end: end:
icu->offset = ch->index; icu->offset = ch->index;

View file

@ -230,7 +230,7 @@ function bjson_test_symbol()
function bjson_test_bytecode() function bjson_test_bytecode()
{ {
var buf, o, r, e; var buf, o, r, e, i;
o = std.evalScript(";(function f(o){ return o.i })", {compile_only: true}); o = std.evalScript(";(function f(o){ return o.i })", {compile_only: true});
buf = bjson.write(o, /*JS_WRITE_OBJ_BYTECODE*/(1 << 0)); buf = bjson.write(o, /*JS_WRITE_OBJ_BYTECODE*/(1 << 0));
@ -241,10 +241,10 @@ function bjson_test_bytecode()
} }
assert(String(e), "SyntaxError: no bytecode allowed"); assert(String(e), "SyntaxError: no bytecode allowed");
// can't really do anything with |o| at the moment,
// no way to pass it to JS_EvalFunction
o = bjson.read(buf, 0, buf.byteLength, /*JS_READ_OBJ_BYTECODE*/(1 << 0)); o = bjson.read(buf, 0, buf.byteLength, /*JS_READ_OBJ_BYTECODE*/(1 << 0));
assert(String(o), "[function bytecode]"); assert(String(o), "[function bytecode]");
o = std.evalScript(o, {eval_function: true});
for (i = 0; i < 42; i++) o({i}); // exercise o.i IC
} }
function bjson_test_fuzz() function bjson_test_fuzz()