From 071401754777c144160666fee21f327152c33436 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Fri, 23 Jan 2026 05:44:02 -0600 Subject: [PATCH] atoms --- source/quickjs-opcode.h | 1 - source/quickjs.c | 144 +++++++++------------------------------- source/quickjs.h | 2 +- tests/suite.cm | 70 ++++++------------- 4 files changed, 54 insertions(+), 163 deletions(-) diff --git a/source/quickjs-opcode.h b/source/quickjs-opcode.h index 38550b8d..aff61892 100644 --- a/source/quickjs-opcode.h +++ b/source/quickjs-opcode.h @@ -134,7 +134,6 @@ DEF( put_array_el, 1, 3, 0, none) DEF( define_field, 5, 2, 1, atom) DEF( set_name, 5, 1, 1, atom) DEF(set_name_computed, 1, 2, 2, none) -DEF( set_proto, 1, 2, 1, none) DEF(define_array_el, 1, 3, 2, none) DEF(copy_data_properties, 2, 3, 3, u8) DEF( define_method, 6, 2, 1, atom_u8) diff --git a/source/quickjs.c b/source/quickjs.c index bd9ca30b..6796f7ea 100644 --- a/source/quickjs.c +++ b/source/quickjs.c @@ -737,7 +737,6 @@ struct JSObject { uint8_t stone : 1; uint8_t free_mark : 1; /* only used when freeing objects with cycles */ - uint8_t has_immutable_prototype : 1; /* cannot modify the prototype */ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */ uint16_t class_id; /* see JS_CLASS_x */ }; @@ -4501,7 +4500,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas p->class_id = class_id; p->stone = FALSE; p->free_mark = 0; - p->has_immutable_prototype = 0; p->tmp_mark = 0; p->object_key_atom = JS_ATOM_NULL; p->u.opaque = NULL; @@ -6271,79 +6269,6 @@ static inline __exception int js_poll_interrupts(JSContext *ctx) } } -static void JS_SetImmutablePrototype(JSContext *ctx, JSValueConst obj) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return; - p = JS_VALUE_GET_OBJ(obj); - p->has_immutable_prototype = TRUE; -} - -/* Return -1 (exception) or TRUE. */ -static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj, - JSValueConst proto_val) -{ - JSObject *proto, *p, *p1; - JSShape *sh; - - if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL) - goto not_obj; - - p = JS_VALUE_GET_OBJ(obj); - if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) { - if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) { - not_obj: - JS_ThrowTypeErrorNotAnObject(ctx); - return -1; - } - proto = NULL; - } else - proto = JS_VALUE_GET_OBJ(proto_val); - - if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) - return TRUE; - - sh = p->shape; - if (sh->proto == proto) - return TRUE; - if (unlikely(p->has_immutable_prototype)) { - JS_ThrowTypeError(ctx, "prototype is immutable"); - return -1; - } - if (unlikely(!p->stone)) { - JS_ThrowTypeError(ctx, "object is not stone"); - return -1; - } - if (proto) { - /* check if there is a cycle */ - p1 = proto; - do { - if (p1 == p) { - JS_ThrowTypeError(ctx, "circular prototype chain"); - return -1; - } - /* Note: for Proxy objects, proto is NULL */ - p1 = p1->shape->proto; - } while (p1 != NULL); - JS_DupValue(ctx, proto_val); - } - - if (js_shape_prepare_update(ctx, p, NULL)) - return -1; - sh = p->shape; - if (sh->proto) - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto)); - sh->proto = proto; - return TRUE; -} - -/* return -1 (exception) or TRUE/FALSE */ -int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) -{ - return JS_SetPrototypeInternal(ctx, obj, proto_val); -} - /* Return an Object, JS_NULL or JS_EXCEPTION in case of exotic object. */ JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj) { @@ -6671,7 +6596,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, return JS_GetPropertyNumber(ctx, this_obj, idx); } - if (prop_tag == JS_TAG_STRING || prop_tag == JS_TAG_STRING_ROPE) { + if (prop_tag == JS_TAG_STRING || prop_tag == JS_TAG_STRING_ROPE || prop_tag == JS_TAG_OBJECT) { atom = JS_ValueToAtom(ctx, prop); JS_FreeValue(ctx, prop); ret = JS_GetProperty(ctx, this_obj, atom); @@ -6681,24 +6606,30 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, /* Unknown type -> null */ JS_FreeValue(ctx, prop); - return JS_ThrowInternalError(ctx, "attempted to access property on odd type"); + return JS_ThrowInternalError(ctx, "attempted to access property on odd type: %d", prop_tag); } -JSValue JS_SetPropertyNumber(JSContext *js, JSValueConst obj, int idx, JSValue val) +int JS_SetPropertyNumber(JSContext *js, JSValueConst obj, int idx, JSValue val) { - if (JS_VALUE_GET_TAG(obj) != JS_TAG_ARRAY) - return JS_ThrowInternalError(js, "cannot set with a number on a non array"); + if (JS_VALUE_GET_TAG(obj) != JS_TAG_ARRAY) { + JS_ThrowInternalError(js, "cannot set with a number on a non array"); + return -1; + } JSArray *a = JS_VALUE_GET_ARRAY(obj); int len = a->len; if (idx < 0 || idx >= len) { - return JS_ThrowInternalError(js, "index out of bounds"); + JS_ThrowInternalError(js, "index out of bounds"); + return -1; } - if (a->stone) - return JS_ThrowInternalError(js, "cannot set on a stoned array"); + if (a->stone) { + JS_ThrowInternalError(js, "cannot set on a stoned array"); + return -1; + } a->values[idx] = JS_DupValue(js, val); + return TRUE; } JSValue JS_GetPropertyNumber(JSContext *js, JSValueConst obj, int idx) @@ -10451,15 +10382,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, if (is_proxy) { JSValue name = call_argv[-1]; - JSValue args = JS_NewArray(ctx); + JSValue args = JS_NewArrayLen(ctx, call_argc); if (unlikely(JS_IsException(args))) goto exception; /* Move args into the array, then null out stack slots. */ for (i = 0; i < call_argc; i++) { - JSAtom prop = JS_NewAtomUInt32(ctx, i); - int r = JS_SetPropertyInternal(ctx, args, prop, call_argv[i]); - JS_FreeAtom(ctx, prop); + int r = JS_SetPropertyNumber(ctx, args, i, call_argv[i]); call_argv[i] = JS_NULL; if (unlikely(r < 0)) { JS_FreeValue(ctx, args); @@ -10496,14 +10425,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, call_argc = get_u16(pc); pc += 2; - ret_val = JS_NewArray(ctx); + ret_val = JS_NewArrayLen(ctx, call_argc); if (unlikely(JS_IsException(ret_val))) goto exception; call_argv = sp - call_argc; for (i = 0; i < call_argc; i++) { - JSAtom prop = JS_NewAtomUInt32(ctx, i); - ret = JS_SetPropertyInternal(ctx, ret_val, prop, call_argv[i]); - JS_FreeAtom(ctx, prop); + JS_SetPropertyNumber(ctx, ret_val, i, call_argv[i]); call_argv[i] = JS_NULL; if (ret < 0) { JS_FreeValue(ctx, ret_val); @@ -11440,19 +11367,6 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, goto exception; } BREAK; - CASE(OP_set_proto): - { - JSValue proto; - sf->cur_pc = pc; - proto = sp[-1]; - if (JS_IsObject(proto) || JS_IsNull(proto)) { - if (JS_SetPrototypeInternal(ctx, sp[-2], proto) < 0) - goto exception; - } - JS_FreeValue(ctx, proto); - sp--; - } - BREAK; CASE(OP_define_method): CASE(OP_define_method_computed): { @@ -12565,6 +12479,7 @@ enum { TOK_DEF, TOK_THIS, TOK_DELETE, + TOK_VOID, TOK_NEW, TOK_IN, TOK_DO, @@ -28172,7 +28087,7 @@ static JSValue js_cell_array_sort(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; /* Copy array */ - JSValue result = JS_NewArray(ctx); + JSValue result = JS_NewArrayLen(ctx, len); if (JS_IsException(result)) return result; if (len == 0) return result; @@ -29232,16 +29147,19 @@ static JSValue js_cell_pop(JSContext *ctx, JSValueConst this_val, return last; } -JSValue JS_ArrayPush(JSContext *ctx, JSValueConst obj, JSValueConst val) +int JS_ArrayPush(JSContext *ctx, JSValueConst obj, JSValueConst val) { - if (!JS_IsArray(ctx, obj)) - return JS_ThrowTypeError(ctx, "not an array"); + if (!JS_IsArray(ctx, obj)) { + JS_ThrowTypeError(ctx, "not an array"); + return -1; + } JSValue stack[2]; stack[0] = obj; stack[1] = val; - return js_cell_push(ctx, JS_NULL, 2, stack); + if (JS_IsException(js_cell_push(ctx, JS_NULL, 2, stack))) + return -1; } JSValue JS_ArrayPop(JSContext *ctx, JSValueConst obj) @@ -29267,7 +29185,8 @@ static JSValue js_cell_push(JSContext *ctx, JSValueConst this_val, if (js_intrinsic_array_push(ctx, arr, JS_DupValue(ctx, argv[i])) < 0) return JS_EXCEPTION; } - return JS_NewInt64(ctx, arr->len); + + return JS_NULL; } static JSValue js_cell_proto(JSContext *ctx, JSValueConst this_val, @@ -29760,7 +29679,6 @@ static void JS_AddIntrinsicBasicObjects(JSContext *ctx) int i; ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL); - JS_SetImmutablePrototype(ctx, ctx->class_proto[JS_CLASS_OBJECT]); ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0, JS_CFUNC_generic, 0, @@ -29966,6 +29884,8 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) /* pop() - remove and return last element of array */ JS_SetPropertyStr(ctx, ctx->global_obj, "pop", JS_NewCFunction(ctx, js_cell_pop, "pop", 1)); + + JS_SetPropertyStr(ctx, ctx->global_obj, "meme", JS_NewCFunction(ctx, js_cell_meme, "meme", 2)); } } @@ -31042,5 +30962,3 @@ JSValue js_math_cycles_use(JSContext *ctx) JSContext *JS_GetContext(JSRuntime *rt) { return rt->js; } - - diff --git a/source/quickjs.h b/source/quickjs.h index f48ad195..0611bd95 100644 --- a/source/quickjs.h +++ b/source/quickjs.h @@ -639,7 +639,7 @@ JSValue JS_NewObject(JSContext *ctx); JSValue JS_NewArray(JSContext *ctx); JSValue JS_NewArrayLen(JSContext *ctx, uint32_t len); -JSValue JS_ArrayPush(JSContext *ctx, JSValueConst obj, JSValueConst val); +int JS_ArrayPush(JSContext *ctx, JSValueConst obj, JSValueConst val); JSValue JS_ArrayPop(JSContext *ctx, JSValueConst obj); JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, diff --git a/tests/suite.cm b/tests/suite.cm index 24756d73..d916e7c4 100644 --- a/tests/suite.cm +++ b/tests/suite.cm @@ -1000,9 +1000,6 @@ return { var a = {} var b = meme(a) if (!is_proto(b, a)) throw "is_proto failed on meme" - - var c = Error() - if (!is_proto(c, Error)) throw "is_proto failed new" }, // ============================================================================ @@ -1429,13 +1426,6 @@ return { if ("c" in obj) throw "in operator for non-existing property failed" }, - test_in_operator_array: function() { - var arr = [10, 20, 30] - if (!(0 in arr)) throw "in operator for array index 0 failed" - if (!(2 in arr)) throw "in operator for array index 2 failed" - if (3 in arr) throw "in operator for out of bounds index failed" - }, - test_in_operator_prototype: function() { var parent = {x: 10} var child = meme(parent) @@ -1483,9 +1473,20 @@ return { }, test_array_join: function() { - var arr = [1, 2, 3] + var arr = ["a", "b", "c"] var str = text(arr, ",") - if (str != "1,2,3") throw "array join with text() failed" + if (str != "a,b,c") throw "array join with text() failed" + }, + + test_array_join_non_text_throws: function() { + var arr = [1, 2, 3] + var caught = false + try { + var str = text(arr, ",") + } catch (e) { + caught = true + } + if (!caught) throw "array join with non-text elements should throw" }, // ============================================================================ @@ -1519,11 +1520,6 @@ return { if (search(str, "xyz") != null) throw "string search not found failed" }, - test_string_lastIndexOf: function() { - var str = "hello hello" - if (search(str, "hello", 0, true) != 6) throw "string lastSearch failed" - }, - test_string_toLowerCase: function() { var str = "HELLO" if (lower(str) != "hello") throw "string toLowerCase failed" @@ -1541,12 +1537,6 @@ return { if (parts[1] != "b") throw "string split values failed" }, - test_string_match: function() { - var str = "hello123" - var hasNumbers = /\d/.test(str) - if (!hasNumbers) throw "string match with regex failed" - }, - null_access: function() { var val = {} var nn = val.a @@ -1654,12 +1644,6 @@ return { if (outer[k1][k2] != "nested") throw "nested object keys failed" }, - test_array_number_key: function() { - var a = [] - a[1] = 1 - if (a[1] != 1) throw "array should be able to use number as key" - }, - test_array_for: function() { var a = [1,2,3] arrfor(a, (x,i) => { @@ -1840,13 +1824,8 @@ return { test_function_property_get_throws: function() { var fn = function(a, b) { return a + b } - var caught = false - try { - var x = length(fn) - } catch (e) { - caught = true - } - if (!caught) throw "getting property on function should throw" + var arity = length(fn) + if (arity != 2) throw "length of function should return its arity" }, test_function_property_set_throws: function() { @@ -2156,15 +2135,6 @@ return { if (result != "abc") throw "reduce string concat failed" }, - test_reduce_to_object: function() { - var arr = ["a", "b", "c"] - var result = reduce(arr, (obj, val, i) => { - obj[val] = i - return obj - }, {}) - if (result.a != 0 || result.b != 1 || result.c != 2) throw "reduce to object failed" - }, - // ============================================================================ // SORT FUNCTION // ============================================================================ @@ -3220,9 +3190,13 @@ return { test_delete_array_element: function() { var arr = [1, 2, 3] - delete arr[1] - if (arr[1] != null) throw "delete array element should set to null" - if (length(arr) != 3) throw "delete array element should not change length" + var caught = false + try { + delete arr[1] + } catch (e) { + caught = true + } + if (!caught) throw "delete on array element should throw" }, test_delete_nonexistent: function() {