diff --git a/quickjs-libc.c b/quickjs-libc.c index e930ff8b5..ae82f3abc 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -832,11 +832,13 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val, { JSRuntime *rt = JS_GetRuntime(ctx); JSThreadState *ts = JS_GetRuntimeOpaque(rt); - const char *str; + const char *str = NULL; size_t len; - JSValue ret; + JSValue ret, obj; JSValue options_obj; BOOL backtrace_barrier = FALSE; + BOOL eval_function = FALSE; + BOOL compile_only = FALSE; BOOL is_async = FALSE; int flags; @@ -845,14 +847,22 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val, if (get_bool_option(ctx, &backtrace_barrier, options_obj, "backtrace_barrier")) 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, + "compile_only")) + return JS_EXCEPTION; if (get_bool_option(ctx, &is_async, options_obj, "async")) return JS_EXCEPTION; } - str = JS_ToCStringLen(ctx, &len, argv[0]); - if (!str) - return JS_EXCEPTION; + if (!eval_function) { + str = JS_ToCStringLen(ctx, &len, argv[0]); + if (!str) + return JS_EXCEPTION; + } if (!ts->recv_pipe && ++ts->eval_script_recurse == 1) { /* install the interrupt handler */ JS_SetInterruptHandler(JS_GetRuntime(ctx), interrupt_handler, NULL); @@ -860,9 +870,16 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val, flags = JS_EVAL_TYPE_GLOBAL; if (backtrace_barrier) flags |= JS_EVAL_FLAG_BACKTRACE_BARRIER; + if (compile_only) + flags |= JS_EVAL_FLAG_COMPILE_ONLY; if (is_async) flags |= JS_EVAL_FLAG_ASYNC; - ret = JS_Eval(ctx, str, len, "", flags); + 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, "", flags); + } JS_FreeCString(ctx, str); if (!ts->recv_pipe && --ts->eval_script_recurse == 0) { /* remove the interrupt handler */ diff --git a/quickjs.c b/quickjs.c index 85735785c..ce6d85a35 100644 --- a/quickjs.c +++ b/quickjs.c @@ -7362,9 +7362,8 @@ static JSValue JS_GetPropertyInternal2(JSContext *ctx, JSValue obj, continue; } } else { - if (icu && proto_depth == 0 && p->shape->is_hashed) { + if (proto_depth == 0) add_ic_slot(ctx, icu, prop, p, offset); - } return js_dup(pr->u.value); } } @@ -8658,9 +8657,7 @@ static int JS_SetPropertyInternal2(JSContext *ctx, JSValue obj, JSAtom prop, if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { /* 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); return TRUE; } else if (prs->flags & JS_PROP_LENGTH) { @@ -35019,7 +35016,8 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) goto fail; if (b->source_len) { bc_read_trace(s, "source: %d bytes\n", b->source_len); - s->ptr_last += b->source_len; // omit source code hex dump + if (s->ptr_last) + s->ptr_last += b->source_len; // omit source code hex dump /* b->source is a UTF-8 encoded null terminated C string */ b->source = js_mallocz(ctx, b->source_len + 1); if (!b->source) @@ -35472,12 +35470,14 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) break; case BC_TAG_FUNCTION_BYTECODE: if (!s->allow_bytecode) - goto invalid_tag; + goto no_allow_bytecode; obj = JS_ReadFunctionTag(s); break; case BC_TAG_MODULE: - if (!s->allow_bytecode) - goto invalid_tag; + if (!s->allow_bytecode) { + no_allow_bytecode: + return JS_ThrowSyntaxError(ctx, "no bytecode allowed"); + } obj = JS_ReadModule(s); break; case BC_TAG_OBJECT: @@ -54513,10 +54513,19 @@ static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, { int32_t i; uint32_t h; - JSInlineCache *ic = icu->ic; JSInlineCacheHashSlot *ch; JSInlineCacheRingSlot *cr; + JSInlineCache *ic; JSShape *sh; + + if (!icu) + return; + ic = icu->ic; + if (!ic) + return; + sh = object->shape; + if (!sh->is_hashed) + return; cr = NULL; h = get_index_hash(atom, ic->hash_bits); for (ch = ic->hash[h]; ch != NULL; ch = ch->next) { @@ -54525,21 +54534,17 @@ static void add_ic_slot(JSContext *ctx, JSInlineCacheUpdate *icu, break; } } - assert(cr != NULL); i = cr->index; - for (;;) { - if (object->shape == cr->shape[i]) { + do { + if (sh == cr->shape[i]) { cr->prop_offset[i] = prop_offset; goto end; } i = (i + 1) % countof(cr->shape); - if (unlikely(i == cr->index)) - break; - } - sh = cr->shape[i]; - cr->shape[i] = js_dup_shape(object->shape); - js_free_shape_null(ctx->rt, sh); + } while (i != cr->index); + js_free_shape_null(ctx->rt, cr->shape[i]); + cr->shape[i] = js_dup_shape(sh); cr->prop_offset[i] = prop_offset; end: icu->offset = ch->index; diff --git a/tests/test_bjson.js b/tests/test_bjson.js index 6a2ee2cbf..a957ceeff 100644 --- a/tests/test_bjson.js +++ b/tests/test_bjson.js @@ -1,3 +1,4 @@ +import * as std from "std"; import * as bjson from "bjson"; import { assert } from "./assert.js"; @@ -227,6 +228,25 @@ function bjson_test_symbol() assert(o, r); } +function bjson_test_bytecode() +{ + var buf, o, r, e, i; + + o = std.evalScript(";(function f(o){ return o.i })", {compile_only: true}); + buf = bjson.write(o, /*JS_WRITE_OBJ_BYTECODE*/(1 << 0)); + try { + bjson.read(buf, 0, buf.byteLength); + } catch (_e) { + e = _e; + } + assert(String(e), "SyntaxError: no bytecode allowed"); + + o = bjson.read(buf, 0, buf.byteLength, /*JS_READ_OBJ_BYTECODE*/(1 << 0)); + 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() { var corpus = [ @@ -277,6 +297,7 @@ function bjson_test_all() bjson_test_map(); bjson_test_set(); bjson_test_symbol(); + bjson_test_bytecode(); bjson_test_fuzz(); }